diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Changelog for harg
 
+## 0.4.2.0 [2020.01.27]
+
+- Add explicit export and import lists
+- Some documentation fixes
+
 ## 0.4.1.0 [2019.12.22]
 
 - Parsers now stop immediately if a source error is encountered
diff --git a/README.lhs b/README.lhs
deleted file mode 100644
--- a/README.lhs
+++ /dev/null
@@ -1,812 +0,0 @@
-# `harg`
-
-[![Build Status](https://travis-ci.org/alexpeits/harg.svg?branch=master)](https://travis-ci.org/alexpeits/harg)
-[![Hackage](https://img.shields.io/hackage/v/harg.svg)](https://hackage.haskell.org/package/harg)
-
-`harg` is a library for configuring programs by scanning command line arguments,
-environment variables, default values and more. Under the hood, it uses a subset
-of [`optparse-applicative`](https://hackage.haskell.org/package/optparse-applicative)
-to expose regular arguments, switch arguments and subcommands. The library
-relies heavily on the use of higher kinded data (HKD) thanks to the
-[`barbies`](https://hackage.haskell.org/package/barbies) library. Using
-[`higgledy`](https://hackage.haskell.org/package/higgledy) also allows to have
-significantly less boilerplate code.
-
-The main goal while developing `harg` was to not have to go through the usual
-pattern of manually `mappend`ing the results of command line parsing, env vars
-and defaults.
-
-# Usage
-
-tl;dr: Take a look at the [example](Example.hs).
-
-Here are some different usage scenarios. Let's first enable some language
-extensions and add some imports:
-
-``` haskell
-{-# LANGUAGE DataKinds          #-}
-{-# LANGUAGE DeriveAnyClass     #-}
-{-# LANGUAGE DeriveGeneric      #-}
-{-# LANGUAGE FlexibleInstances  #-}
-{-# LANGUAGE GADTs              #-}
-{-# LANGUAGE KindSignatures     #-}
-{-# LANGUAGE StandaloneDeriving #-}
-{-# LANGUAGE TypeApplications   #-}
-{-# LANGUAGE TypeOperators      #-}
-
-import           Data.Functor.Identity (Identity (..))
-import           Data.Kind             (Type)
-import           GHC.Generics          (Generic)
-
-import qualified Data.Barbie           as B
-import           Data.Aeson            (FromJSON)
-import           Data.Generic.HKD      (HKD, build, construct)
-
-import           Options.Harg
-
-main :: IO ()
-main = putStrLn "this is a literate haskell file"
-```
-
-## One flat (non-nested) datatype
-
-The easiest scenario is when the target configuration type is one single record
-with no levels of nesting:
-
-``` haskell
-data FlatConfig
-  = FlatConfig
-      { _fcDbHost :: String
-      , _fcDbPort :: Int
-      , _fcDir    :: String
-      , _fcLog    :: Bool  -- whether to log or not
-      }
-  deriving (Show, Generic)
-```
-
-(The `Generic` instance is required for section `3` later on)
-
-Let's first create the `Opt`s for each value in `FlatConfig`. `Opt` is the
-description for each component of the configuration.
-
-``` haskell
-dbHostOpt :: Opt String
-dbHostOpt
-  = option strParser
-      ( long "host"
-      . short 'h'
-      . metavar "DB_HOST"
-      . help "The database host"
-      )
-
-dbPortOpt :: Opt Int
-dbPortOpt
-  = option readParser
-      ( long "port"
-      . help "The database port"
-      . envVar "DB_PORT"
-      . defaultVal 5432
-      )
-
-dirOpt :: Opt String
-dirOpt
-  = argument strParser
-      ( help "Some directory"
-      . defaultVal "/home/user/something"
-      )
-
-logOpt :: Opt Bool
-logOpt
-  = switch
-      ( long "log"
-      . help "Whether to log or not"
-      )
-```
-
-Here, we use `option` to define a command line argument that expects a value
-after it, `argument` to define a standalone argument, not prefixed by a long or
-short indicator, and `switch` to define a boolean command line flag that, if
-present, sets the target value to `True`. The `opt*` functions (here applied
-using `&` to make things look more declarative) modify the option configuration.
-`help` adds help text, `defaultVal` adds a default value, `short` adds a short
-command line option as an alternative to the long one (the string after `option`
-or `switch`), `envVar` sets the associated environment variable and `metavar`
-sets the metavariable to be shown in the help text generated by
-`optparse-applicative`.
-
-
-The first argument (`strParser` or `readParser`) is the parser for the argument,
-be it from the command line or from an environment variable. The type of this
-function should be `String -> Either String a`, which produces an error message
-or the parsed value. `strParser` is equivalent to `pure` and always succeeds.
-`readParser` requires the type to have a `Read` constraint. In order to use it
-with newtypes that wrap a type that has a `Read` constraint, using the `Functor`
-instance for `Opt` should be sufficient. E.g. for the newtype:
-
-``` haskell
-newtype Port = Port Int
-```
-
-we can define the following option:
-
-``` haskell
-dbPortOpt' :: Opt Port
-dbPortOpt'
-  = Port <$> dbPortOpt
-```
-
-Of course, any user-defined function works as well. In addition, to use a
-function of type `String -> Maybe a` use `parseWith`, which runs the parser and
-in case of failure uses a default error message. For example, `readParser` is
-defined as `parseWith readMaybe`.
-
-Finally, an optional option with type `a` can be specified by setting its type
-to `Maybe a`. The declaration is exactly the same as it would be for `a`, and
-adding `optional` to the modifiers turns turns the parser from `String -> Either
-String a` to `String -> Either String (Maybe a)` but without using the `Read`
-instance for `Maybe`:
-
-``` haskell ignore
-someOpt :: Opt (Maybe Int)
-someOpt
-  = option readParser
-      ( long "something"
-      . optional
-      )
-```
-
-Note that `optional` can't be used with `defaultVal`. Using them together raises
-a type error at compile time, to ensure there's no ambiguous behaviour (e.g. the
-order of declaration of modifiers should not influence the resulting option).
-
-There are 3 ways to configure this datatype.
-
-### 1. Using a `barbie` type
-
-`barbie` types are types of kind `(Type -> Type) -> Type`. The `barbie` type for
-`FlatConfig` looks like this:
-
-``` haskell
-data FlatConfigB f
-  = FlatConfigB
-      { _fcDbHostB :: f String
-      , _fcDbPortB :: f Int
-      , _fcDirB    :: f String
-      , _fcLogB    :: f Bool
-      }
-  deriving (Generic, B.FunctorB, B.TraversableB, B.ProductB)
-```
-
-I also derived some required instances that come from the `barbies` package.
-These instances allow us to change the `f` (`bmap` from `FunctorB`) and traverse
-all types in the record producing side effects (`btraverse` from
-`TraversableB`).
-
-Now let's define the value of this datatype, which holds our option
-configuration. The type constructor needed for the options is `Opt`:
-
-``` haskell
-flatConfigOpt1 :: FlatConfigB Opt
-flatConfigOpt1
-  = FlatConfigB dbHostOpt dbPortOpt dirOpt logOpt
-```
-
-Because `dbHostOpt`, `dbPortOpt` and `logOpt` all have type `Opt <actual type>`,
-`flatConfigOpt1` has the correct type according to `FlatConfigB Opt`.
-
-Now to actually run things:
-
-``` haskell
-getFlatConfig1 :: IO ()
-getFlatConfig1 = do
-  FlatConfigB host port dir log <- execOptDef flatConfigOpt1
-  print $ runIdentity $
-    FlatConfig
-    <$> host
-    <*> port
-    <*> dir
-    <*> log
-```
-
-`execOpt` returns an `Identity x` where `x` is the type of the options we are
-configuring, in this case `FlatConfigB`. Here, we pattern match on the
-barbie-type, and then use the `Applicative` instance of `Identity` to get back
-an `Identity FlatConfig`.
-
-This is still a bit boilerplate-y. Let's look at another way.
-
-### 2. Using a product type
-
-Looking at `FlatConfigB`, it's only used because of it's `barbie`-like
-capabilities. Other than that it's just a simple product type with the
-additional `f` before all its sub-types.
-
-`harg` defines a type almost similar to `Product` (from `Data.Functor.Product`),
-which works in a similar fashion as servant's `:<|>` type. This type is defined
-in `Options.Harg.Het.Prod` and is called `:*` (the `*` stands for product). This
-type stores barbie-like types and also keeps the `f` handy: `data (a :* b) f = a
-f :* b f`. This is also easily made an instance of `Generic`, `FunctorB`,
-`TraversableB` and `ProductB`. With all that, let's rewrite the options value
-and the function to get the configuration:
-
-``` haskell
-flatConfigOpt2
-  :: (Single String :* Single Int :* Single String :* Single Bool) Opt
-flatConfigOpt2
-  = single dbHostOpt :* single dbPortOpt :* single dirOpt :* single logOpt
-
-getFlatConfig2 :: IO ()
-getFlatConfig2 = do
-  host :* port :* dir :* log <- execOptDef flatConfigOpt2
-  print $ runIdentity $
-    FlatConfig
-    <$> getSingle host
-    <*> getSingle port
-    <*> getSingle dir
-    <*> getSingle log
-```
-
-This looks aufully similar to the previous version, but without having to write
-another datatype and derive all the instances. `:*` is both a type-level
-constructor and a value-level function that acts like list's `:`. It is also
-right-associative, so for example `a :* b :* c` is the same as `a :* (b :* c)`.
-
-The `Single` type constructor is used when talking about a single value, rather
-than a nested datatype. `Single a f` is a simple newtype over `f a`. The reason
-for using that is simply to switch the order of application, so that we can
-later apply the `f` (here `Opt`) to the compound type (`:*`). This makes type
-definitions look more similar to datatype definitions:
-
-``` haskell
-type FlatConfigOpt2
-  =  Single String
-  :* Single Int
-  :* Single Bool
-```
-
-In addition, `single` is used to wrap an `f a` into a `Single a f`, and
-`getSingle` is used to unwrap it. Later on we'll see how to construct nested
-configurations using `Nested`.
-
-However, the real value when having flat datatypes comes from the ability to use
-`higgledy`.
-
-### 3. Using `HKD` from `higgledy`
-
-``` haskell
-flatConfigOpt3 :: HKD FlatConfig Opt
-flatConfigOpt3
-  = build @FlatConfig dbHostOpt dbPortOpt dirOpt logOpt
-
-getFlatConfig3 :: IO ()
-getFlatConfig3 = do
-  result <- execOptDef flatConfigOpt3
-  print $ runIdentity (construct result)
-```
-
-This is the most straightforward way to work with flat configuration types. The
-`build` function takes as arguments the options (`Opt a` where `a` is each type
-in `FlatConfig`) **in the order they appear in the datatype**, and returns the
-generic representation of a type that's exactly the same as `FlatConfigB`. This
-means that we get all the `barbie` instances for free.
-
-To go back from the `HKD` representation of a datatype to the base one, we use
-`construct`. `construct` uses the applicative instance of the `f` which wraps
-each type in `FlatConfig` to give back an `f FlatConfig` (in our case an
-`Identity FlatConfig`).
-
-## Nested datatypes
-
-Let's say now that we have these two datatypes:
-
-``` haskell
-data DbConfig
-  = DbConfig
-      { _dcHost :: String
-      , _dcPort :: Int
-      }
-  deriving (Show, Generic)
-
-data ServiceConfig
-  = ServiceConfig
-      { _scPort :: Int
-      , _scLog  :: Bool
-      }
-  deriving (Show, Generic)
-```
-
-And the datatype to be configured is this:
-
-``` haskell
-data Config
-  = Config
-      { _cDb      :: DbConfig
-      , _cService :: ServiceConfig
-      , _cDir     :: String
-      }
-  deriving (Show, Generic)
-```
-
-And a new option required for the service port:
-
-``` haskell
-portOpt :: Opt Int
-portOpt
-  = option readParser
-      ( long "port"
-      . help "The service port"
-      . defaultVal 8080
-      )
-```
-
-Again, there are several ways to configure these options.
-
-### 1. Using `barbie` types
-
-Since we now have 3 types, there's a bit more boilerplate to write:
-
-``` haskell
-data ConfigB f
-  = ConfigB
-      { _cDbB      :: DbConfigB f
-      , _cServiceB :: ServiceConfigB f
-      , _cDirB     :: f String
-      }
-  deriving (Generic, B.FunctorB, B.TraversableB, B.ProductB)
-
-data DbConfigB f
-  = DbConfigB
-      { _dcHostB :: f String
-      , _dcPortB :: f Int
-      }
-  deriving (Generic, B.FunctorB, B.TraversableB, B.ProductB)
-
-data ServiceConfigB f
-  = ServiceConfigB
-      { _scPortB :: f Int
-      , _scLogB  :: f Bool
-      }
-  deriving (Generic, B.FunctorB, B.TraversableB, B.ProductB)
-```
-
-To define the option parser, we need option parsers for every type inside it.
-This was true for flat configs too, but we have to manually construct a
-`DbConfigB Opt` and `ServiceConfigB Opt`:
-
-``` haskell
-configOpt1 :: ConfigB Opt
-configOpt1
-  = ConfigB dbOpt serviceOpt dirOpt
-
-dbOpt :: DbConfigB Opt
-dbOpt
-  = DbConfigB dbHostOpt dbPortOpt
-
-serviceOpt :: ServiceConfigB Opt
-serviceOpt
-  = ServiceConfigB portOpt logOpt
-```
-
-And to run the parser:
-
-``` haskell
-getConfig1 :: IO ()
-getConfig1 = do
-  ConfigB (DbConfigB dbHost dbPort) (ServiceConfigB port log) dir <-
-    execOptDef configOpt1
-  let
-    db      = DbConfig <$> dbHost <*> dbPort
-    service = ServiceConfig <$> port <*> log
-  print $ runIdentity $
-    Config
-    <$> db
-    <*> service
-    <*> dir
-```
-
-### 2. Using `higgledy`
-
-`higgledy` puts an `f` before every type, so doing something like `HKD Config f`
-doesn't make sense: looking at `ConfigB` it seems like the `f` needs to go to
-the right hand side of the nested types. We can, however, avoid the boilerplate
-of defining `barbie` types for the nested datatypes:
-
-``` haskell
-data ConfigH f
-  = ConfigH
-      { _cDbH      :: HKD DbConfig f
-      , _cServiceH :: HKD ServiceConfig f
-      , _cDirH     :: f String
-      }
-  deriving (Generic, B.FunctorB, B.TraversableB, B.ProductB)
-
-configOpt2 :: ConfigH Opt
-configOpt2
-  = ConfigH dbOptH serviceOptH dirOpt
-
-dbOptH :: HKD DbConfig Opt
-dbOptH
-  = build @DbConfig dbHostOpt dbPortOpt
-
-serviceOptH :: HKD ServiceConfig Opt
-serviceOptH
-  = build @ServiceConfig portOpt logOpt
-```
-
-And to run the parser:
-
-``` haskell
-getConfig2 :: IO ()
-getConfig2 = do
-  ConfigH db service dir <- execOptDef configOpt2
-  print $ runIdentity $
-    Config
-    <$> construct db
-    <*> construct service
-    <*> dir
-```
-
-### 2. Using products
-
-Recall from previously that there's the `Single` type which in general turns `f
-b` into `b f`. This means that, by using `Single` for the directory option, all
-`f`s are after their types, so we can just use `:*` instead of having to declare
-a new datatype:
-
-``` haskell
-type ConfigP
-  =  HKD DbConfig
-  :* HKD ServiceConfig
-  :* Single String
-
-configOpt3 :: ConfigP Opt
-configOpt3
-  = dbOptH :* serviceOptH :* single dirOpt
-
-getConfig3 :: IO ()
-getConfig3 = do
-  db :* service :* dir <- execOptDef configOpt3
-  print $ runIdentity $
-    Config
-    <$> construct db
-    <*> construct service
-    <*> getSingle dir
-```
-
-And, to make things look more orthogonal, `harg` defines a type called `Nested`,
-which is exactly the same as `HKD`. There are functions that correspond to
-`build` and `construct`, too:
-
-```
-Nested    <-> HKD
-nested    <-> build
-getNested <-> construct
-```
-
-This means that the previous code block might as well be:
-
-``` haskell
-type ConfigP'
-  =  Nested DbConfig
-  :* Nested ServiceConfig
-  :* Single String
-
-configOpt4 :: ConfigP' Opt
-configOpt4
-  = dbOptN :* serviceOptN :* single dirOpt
-  where
-    dbOptN
-      = nested @DbConfig dbHostOpt dbPortOpt
-    serviceOptN
-      = nested @ServiceConfig portOpt logOpt
-
-getConfig4 :: IO ()
-getConfig4 = do
-  db :* service :* dir <- execOptDef configOpt4
-  print $ runIdentity $
-    Config
-    <$> getNested db
-    <*> getNested service
-    <*> getSingle dir
-```
-
-Pretty cool.
-
-## Subcommands
-
-`harg` also supports (somewhat limited) subcommands, again by using `optparse-applicative`
-underneath.
-
-Because of limitations with higher kinded data when it comes to sum types,
-`harg` uses a different way to define subcommands. `optparse-applicative` allows
-defining subcommands that result to the same type, which means the user needs to
-define a sum type, and each subcommand results in a different constructor. In
-contrast, `harg` defines subcommands that can return completely different types.
-Instead of the result being a sum type, where the user has to pattern match on
-constructors, the result is a `Variant`, which is defined (almost) like this:
-
-``` haskell
-data Variant (xs :: [Type]) where
-  Here :: x -> Variant (x ': xs)
-  There :: Variant xs -> Variant (y ': xs)
-```
-
-`Variant` is like a sum type which holds all the summands in a type-level list.
-Instead of pattern matching in `Left` or `Right` like when using `Either`, we
-pattern match on `Here x`, `There (Here x)` etc. For a pretty thorough
-introduction to `Variant` and more heterogeneous types, check out
-[this repo](https://github.com/i-am-tom/learn-me-a-haskell) by
-[i-am-tom](https://github.com/i-am-tom).
-
-``` haskell
-x :: Variant '[Int, Bool, Char]
-x = There (Here True)
-
-run :: Variant '[Int, Bool, Char] -> Maybe Bool
-run (Here _)                 = Nothing
-run (There (Here b))         = Just b
-run (There (There (Here _))) = Nothing
-
--- > run x
--- Just True
-```
-
-`harg` defines another kind of variant called `VariantF`:
-
-``` haskell ignore
-data VariantF (xs :: [(Type -> Type) -> Type]) (f :: Type -> Type) where
-```
-
-to hold a type-level list of `barbie` types and the `f` to wrap every type with.
-
-To define a type to be used in a subcommand parser we need the target type and
-the subcommand name, which is encoded as a type-level string `Symbol`. There's a
-handy way to define this. Suppose that the the `Config` type above is the
-configuration type when the command is `app` and another type, e.g.`TestConfig`
-is the configuration when the command is `test`:
-
-``` haskell
-data TestConfig
-  = TestConfig
-      { _tcFoo :: String
-      , _tcBar :: Int
-      }
-  deriving Show
-
-fooOpt :: Opt String
-fooOpt
-  = option strParser
-      ( short 'f'
-      . help "Something foo"
-      . defaultVal "this is the default foo"
-      )
-
-barOpt :: Opt Int
-barOpt
-  = option readParser
-      ( short 'b'
-      . help "Something bar"
-      . defaultVal 42
-      )
-
-type TestConfigP
-  = Single String :* Single Int
-
-testConfigOpt :: TestConfigP Opt
-testConfigOpt
-  = single fooOpt :* single barOpt
-```
-
-The subcommand type looks like this:
-
-``` haskell
-type SubcommandConfig
-  =  "app" :-> ConfigP'
-  :+ "test" :-> TestConfigP
-```
-
-The `+` here stands for sum. The associated option type is:
-
-``` haskell
-subcommandOpt :: SubcommandConfig Opt
-subcommandOpt
-  = configOpt4 :+ testConfigOpt :+ ANil
-```
-
-The `ANil` here marks the end of the association list (which is a heterogeneous
-list that associates symbols with types).
-
-Here's how to run this parser:
-
-``` haskell
-getSubcommand :: IO ()
-getSubcommand = do
-  result <- execCommandsDef subcommandOpt
-  case result of
-    HereF (db :* service :* dir)
-      -> print $ runIdentity $
-           Config
-           <$> getNested db
-           <*> getNested service
-           <*> getSingle dir
-    ThereF (HereF (foo :* bar))
-      -> print $ runIdentity $
-           TestConfig
-           <$> getSingle foo
-           <*> getSingle bar
-```
-
-Or use `fromVariantF`, which is similar to the `either` function:
-
-``` haskell
-getSubcommand' :: IO ()
-getSubcommand' = do
-  result <- execCommandsDef subcommandOpt
-  fromVariantF result
-    (\(db :* service :* dir)
-       -> print $ runIdentity $
-            Config
-            <$> getNested db
-            <*> getNested service
-            <*> getSingle dir
-    )
-    (\(foo :* bar)
-       -> print $ runIdentity $
-            TestConfig
-            <$> getSingle foo
-            <*> getSingle bar
-    )
-```
-
-The type of `fromVariantF` can be thought of as being:
-
-``` haskell ignore
-fromVariantF
-  :: VariantF '[a, b, c, ...] f
-  -> (a f -> r)
-  -> (b f -> r)
-  -> (c f -> r)
-  -> ...
-  -> r
-```
-
-The signature will accept the appropriate number of functions depending on the
-length of the type level list.
-
-## More than just environment variables
-
-You may have noticed the use of `execOptDef` and `execCommandsDef` in all of the
-examples up to now. There are actually more configurable versions of these,
-called `execOpt` and `execCommands` respectively. With these functions the user
-can select where to get options from. For example, `execOptDef` is a shorthand
-for `execOpt EnvSource`, which means that options will be fetched from
-environment variables only (along with the command line, which is always
-required, and defaults, which can be optionally provided by the user).
-
-The sources currently supported are environment variables, json and yaml files.
-
-### Configuring using a json file
-
-First of all, let's use `FlatConfig` from the first example:
-
-``` haskell ignore
-data FlatConfig
-  = FlatConfig
-      { _fcDbHost :: String
-      , _fcDbPort :: Int
-      , _fcDir    :: String
-      , _fcLog    :: Bool  -- whether to log or not
-      }
-  deriving (Show, Generic)
-
-dbHostOpt :: Opt String
-dbHostOpt
-  = option strParser
-      ( long "host"
-      . short 'h'
-      . metavar "DB_HOST"
-      . help "The database host"
-      )
-
-dbPortOpt :: Opt Int
-dbPortOpt
-  = option readParser
-      ( long "port"
-      . help "The database port"
-      . envVar "DB_PORT"
-      . defaultVal 5432
-      )
-
-dirOpt :: Opt String
-dirOpt
-  = argument strParser
-      ( help "Some directory"
-      . defaultVal "/home/user/something"
-      )
-
-logOpt :: Opt Bool
-logOpt
-  = switch
-      ( long "log"
-      . help "Whether to log or not"
-      )
-
-flatConfigOpt3 :: HKD FlatConfig Opt
-flatConfigOpt3
-  = build @FlatConfig dbHostOpt dbPortOpt dirOpt logOpt
-```
-
-To use the JSON source, a `FromJSON` instance is required. Thankfully that's
-easy, since `FlatConfig` has `Generic` instance:
-
-``` haskell
-instance FromJSON FlatConfig
-```
-
-In `harg`, sources are defined as products (using `:*`) of options, which means
-that the definition of the sources is not very different than defining options!
-If we only needed the environment variable source, the options would be:
-
-``` haskell ignore
-envSource :: EnvSource Opt
-envSource = EnvSource
-```
-
-There's no need to actually define an option for the environment because there's
-no meaningful configuration for this. To use the `EnvSource` along with a json
-config, we use the following option:
-
-``` haskell
-sourceOpt :: (EnvSource :* JSONSource) Opt
-sourceOpt
-  = EnvSource :* JSONSource jsonOpt
-  where
-    jsonOpt :: Opt ConfigFile
-    jsonOpt
-      = option strParser
-          ( long "json"
-          . short 'j'
-          . help "JSON config filepath"
-          )
-```
-
-Here, the type of the option for the JSON source is `ConfigFile`. This type is a
-wrapper around `FilePath`, which looks like this:
-
-``` haskell ignore
-data ConfigFile
-  = ConfigFile FilePath
-  | NoConfigFile
-```
-
-This has the advantage that, if the user wants to specify an optional
-configuration file, they can simply say:
-
-``` haskell
-jsonOpt :: Opt ConfigFile
-jsonOpt
-  = option strParser
-      ( long "json"
-      . defaultVal NoConfigFile
-      )
-```
-
-Also, because `ConfigFile` has an `IsString` instance, there's no need to say
-`long (ConfigFile "json")` (if `OverloadedStrings` is enabled).
-
-There's a bit of a disconnect between `ConfigFile` and the ability to make
-optional options using `Maybe` and `optional`. The reason for it is that the
-type that `JSONSource` wraps is not polymorphic, since it needs to be a filepath
-specifically.
-
-# Roadmap
-
-- Better errors using `optparse-applicative`'s internals
-- Allow user to pass `optparse-applicative` preferences
-- Write tests
-
-# Credits
-
-- [jcpetruzza](https://github.com/jcpetruzza)
-- [i-am-tom](https://github.com/i-am-tom)
-- [jmackie](https://github.com/jmackie)
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# `harg`
+# harg :nut_and_bolt:
 
 [![Build Status](https://travis-ci.org/alexpeits/harg.svg?branch=master)](https://travis-ci.org/alexpeits/harg)
 [![Hackage](https://img.shields.io/hackage/v/harg.svg)](https://hackage.haskell.org/package/harg)
@@ -9,803 +9,23 @@
 to expose regular arguments, switch arguments and subcommands. The library
 relies heavily on the use of higher kinded data (HKD) thanks to the
 [`barbies`](https://hackage.haskell.org/package/barbies) library. Using
-[`higgledy`](https://hackage.haskell.org/package/higgledy) also allows to have
-significantly less boilerplate code.
-
-The main goal while developing `harg` was to not have to go through the usual
-pattern of manually `mappend`ing the results of command line parsing, env vars
-and defaults.
-
-# Usage
-
-tl;dr: Take a look at the [example](Example.hs).
-
-Here are some different usage scenarios. Let's first enable some language
-extensions and add some imports:
-
-``` haskell
-{-# LANGUAGE DataKinds          #-}
-{-# LANGUAGE DeriveAnyClass     #-}
-{-# LANGUAGE DeriveGeneric      #-}
-{-# LANGUAGE FlexibleInstances  #-}
-{-# LANGUAGE GADTs              #-}
-{-# LANGUAGE KindSignatures     #-}
-{-# LANGUAGE StandaloneDeriving #-}
-{-# LANGUAGE TypeApplications   #-}
-{-# LANGUAGE TypeOperators      #-}
-
-import           Data.Functor.Identity (Identity (..))
-import           Data.Kind             (Type)
-import           GHC.Generics          (Generic)
-
-import qualified Data.Barbie           as B
-import           Data.Aeson            (FromJSON)
-import           Data.Generic.HKD      (HKD, build, construct)
-
-import           Options.Harg
-
-main :: IO ()
-main = putStrLn "this is a literate haskell file"
-```
-
-## One flat (non-nested) datatype
-
-The easiest scenario is when the target configuration type is one single record
-with no levels of nesting:
-
-``` haskell
-data FlatConfig
-  = FlatConfig
-      { _fcDbHost :: String
-      , _fcDbPort :: Int
-      , _fcDir    :: String
-      , _fcLog    :: Bool  -- whether to log or not
-      }
-  deriving (Show, Generic)
-```
-
-(The `Generic` instance is required for section `3` later on)
-
-Let's first create the `Opt`s for each value in `FlatConfig`. `Opt` is the
-description for each component of the configuration.
-
-``` haskell
-dbHostOpt :: Opt String
-dbHostOpt
-  = option strParser
-      ( long "host"
-      . short 'h'
-      . metavar "DB_HOST"
-      . help "The database host"
-      )
-
-dbPortOpt :: Opt Int
-dbPortOpt
-  = option readParser
-      ( long "port"
-      . help "The database port"
-      . envVar "DB_PORT"
-      . defaultVal 5432
-      )
-
-dirOpt :: Opt String
-dirOpt
-  = argument strParser
-      ( help "Some directory"
-      . defaultVal "/home/user/something"
-      )
-
-logOpt :: Opt Bool
-logOpt
-  = switch
-      ( long "log"
-      . help "Whether to log or not"
-      )
-```
-
-Here, we use `option` to define a command line argument that expects a value
-after it, `argument` to define a standalone argument, not prefixed by a long or
-short indicator, and `switch` to define a boolean command line flag that, if
-present, sets the target value to `True`. The `opt*` functions (here applied
-using `&` to make things look more declarative) modify the option configuration.
-`help` adds help text, `defaultVal` adds a default value, `short` adds a short
-command line option as an alternative to the long one (the string after `option`
-or `switch`), `envVar` sets the associated environment variable and `metavar`
-sets the metavariable to be shown in the help text generated by
-`optparse-applicative`.
-
-
-The first argument (`strParser` or `readParser`) is the parser for the argument,
-be it from the command line or from an environment variable. The type of this
-function should be `String -> Either String a`, which produces an error message
-or the parsed value. `strParser` is equivalent to `pure` and always succeeds.
-`readParser` requires the type to have a `Read` constraint. In order to use it
-with newtypes that wrap a type that has a `Read` constraint, using the `Functor`
-instance for `Opt` should be sufficient. E.g. for the newtype:
-
-``` haskell
-newtype Port = Port Int
-```
-
-we can define the following option:
-
-``` haskell
-dbPortOpt' :: Opt Port
-dbPortOpt'
-  = Port <$> dbPortOpt
-```
-
-Of course, any user-defined function works as well. In addition, to use a
-function of type `String -> Maybe a` use `parseWith`, which runs the parser and
-in case of failure uses a default error message. For example, `readParser` is
-defined as `parseWith readMaybe`.
-
-Finally, an optional option with type `a` can be specified by setting its type
-to `Maybe a`. The declaration is exactly the same as it would be for `a`, and
-adding `optional` to the modifiers turns turns the parser from `String -> Either
-String a` to `String -> Either String (Maybe a)` but without using the `Read`
-instance for `Maybe`:
-
-``` haskell ignore
-someOpt :: Opt (Maybe Int)
-someOpt
-  = option readParser
-      ( long "something"
-      . optional
-      )
-```
-
-Note that `optional` can't be used with `defaultVal`. Using them together raises
-a type error at compile time, to ensure there's no ambiguous behaviour (e.g. the
-order of declaration of modifiers should not influence the resulting option).
-
-There are 3 ways to configure this datatype.
-
-### 1. Using a `barbie` type
-
-`barbie` types are types of kind `(Type -> Type) -> Type`. The `barbie` type for
-`FlatConfig` looks like this:
-
-``` haskell
-data FlatConfigB f
-  = FlatConfigB
-      { _fcDbHostB :: f String
-      , _fcDbPortB :: f Int
-      , _fcDirB    :: f String
-      , _fcLogB    :: f Bool
-      }
-  deriving (Generic, B.FunctorB, B.TraversableB, B.ProductB)
-```
-
-I also derived some required instances that come from the `barbies` package.
-These instances allow us to change the `f` (`bmap` from `FunctorB`) and traverse
-all types in the record producing side effects (`btraverse` from
-`TraversableB`).
-
-Now let's define the value of this datatype, which holds our option
-configuration. The type constructor needed for the options is `Opt`:
-
-``` haskell
-flatConfigOpt1 :: FlatConfigB Opt
-flatConfigOpt1
-  = FlatConfigB dbHostOpt dbPortOpt dirOpt logOpt
-```
-
-Because `dbHostOpt`, `dbPortOpt` and `logOpt` all have type `Opt <actual type>`,
-`flatConfigOpt1` has the correct type according to `FlatConfigB Opt`.
-
-Now to actually run things:
-
-``` haskell
-getFlatConfig1 :: IO ()
-getFlatConfig1 = do
-  FlatConfigB host port dir log <- execOptDef flatConfigOpt1
-  print $ runIdentity $
-    FlatConfig
-    <$> host
-    <*> port
-    <*> dir
-    <*> log
-```
-
-`execOpt` returns an `Identity x` where `x` is the type of the options we are
-configuring, in this case `FlatConfigB`. Here, we pattern match on the
-barbie-type, and then use the `Applicative` instance of `Identity` to get back
-an `Identity FlatConfig`.
-
-This is still a bit boilerplate-y. Let's look at another way.
-
-### 2. Using a product type
-
-Looking at `FlatConfigB`, it's only used because of it's `barbie`-like
-capabilities. Other than that it's just a simple product type with the
-additional `f` before all its sub-types.
-
-`harg` defines a type almost similar to `Product` (from `Data.Functor.Product`),
-which works in a similar fashion as servant's `:<|>` type. This type is defined
-in `Options.Harg.Het.Prod` and is called `:*` (the `*` stands for product). This
-type stores barbie-like types and also keeps the `f` handy: `data (a :* b) f = a
-f :* b f`. This is also easily made an instance of `Generic`, `FunctorB`,
-`TraversableB` and `ProductB`. With all that, let's rewrite the options value
-and the function to get the configuration:
-
-``` haskell
-flatConfigOpt2
-  :: (Single String :* Single Int :* Single String :* Single Bool) Opt
-flatConfigOpt2
-  = single dbHostOpt :* single dbPortOpt :* single dirOpt :* single logOpt
-
-getFlatConfig2 :: IO ()
-getFlatConfig2 = do
-  host :* port :* dir :* log <- execOptDef flatConfigOpt2
-  print $ runIdentity $
-    FlatConfig
-    <$> getSingle host
-    <*> getSingle port
-    <*> getSingle dir
-    <*> getSingle log
-```
-
-This looks aufully similar to the previous version, but without having to write
-another datatype and derive all the instances. `:*` is both a type-level
-constructor and a value-level function that acts like list's `:`. It is also
-right-associative, so for example `a :* b :* c` is the same as `a :* (b :* c)`.
-
-The `Single` type constructor is used when talking about a single value, rather
-than a nested datatype. `Single a f` is a simple newtype over `f a`. The reason
-for using that is simply to switch the order of application, so that we can
-later apply the `f` (here `Opt`) to the compound type (`:*`). This makes type
-definitions look more similar to datatype definitions:
-
-``` haskell
-type FlatConfigOpt2
-  =  Single String
-  :* Single Int
-  :* Single Bool
-```
-
-In addition, `single` is used to wrap an `f a` into a `Single a f`, and
-`getSingle` is used to unwrap it. Later on we'll see how to construct nested
-configurations using `Nested`.
-
-However, the real value when having flat datatypes comes from the ability to use
-`higgledy`.
-
-### 3. Using `HKD` from `higgledy`
-
-``` haskell
-flatConfigOpt3 :: HKD FlatConfig Opt
-flatConfigOpt3
-  = build @FlatConfig dbHostOpt dbPortOpt dirOpt logOpt
-
-getFlatConfig3 :: IO ()
-getFlatConfig3 = do
-  result <- execOptDef flatConfigOpt3
-  print $ runIdentity (construct result)
-```
-
-This is the most straightforward way to work with flat configuration types. The
-`build` function takes as arguments the options (`Opt a` where `a` is each type
-in `FlatConfig`) **in the order they appear in the datatype**, and returns the
-generic representation of a type that's exactly the same as `FlatConfigB`. This
-means that we get all the `barbie` instances for free.
-
-To go back from the `HKD` representation of a datatype to the base one, we use
-`construct`. `construct` uses the applicative instance of the `f` which wraps
-each type in `FlatConfig` to give back an `f FlatConfig` (in our case an
-`Identity FlatConfig`).
-
-## Nested datatypes
-
-Let's say now that we have these two datatypes:
-
-``` haskell
-data DbConfig
-  = DbConfig
-      { _dcHost :: String
-      , _dcPort :: Int
-      }
-  deriving (Show, Generic)
-
-data ServiceConfig
-  = ServiceConfig
-      { _scPort :: Int
-      , _scLog  :: Bool
-      }
-  deriving (Show, Generic)
-```
-
-And the datatype to be configured is this:
-
-``` haskell
-data Config
-  = Config
-      { _cDb      :: DbConfig
-      , _cService :: ServiceConfig
-      , _cDir     :: String
-      }
-  deriving (Show, Generic)
-```
-
-And a new option required for the service port:
-
-``` haskell
-portOpt :: Opt Int
-portOpt
-  = option readParser
-      ( long "port"
-      . help "The service port"
-      . defaultVal 8080
-      )
-```
-
-Again, there are several ways to configure these options.
-
-### 1. Using `barbie` types
-
-Since we now have 3 types, there's a bit more boilerplate to write:
-
-``` haskell
-data ConfigB f
-  = ConfigB
-      { _cDbB      :: DbConfigB f
-      , _cServiceB :: ServiceConfigB f
-      , _cDirB     :: f String
-      }
-  deriving (Generic, B.FunctorB, B.TraversableB, B.ProductB)
-
-data DbConfigB f
-  = DbConfigB
-      { _dcHostB :: f String
-      , _dcPortB :: f Int
-      }
-  deriving (Generic, B.FunctorB, B.TraversableB, B.ProductB)
-
-data ServiceConfigB f
-  = ServiceConfigB
-      { _scPortB :: f Int
-      , _scLogB  :: f Bool
-      }
-  deriving (Generic, B.FunctorB, B.TraversableB, B.ProductB)
-```
-
-To define the option parser, we need option parsers for every type inside it.
-This was true for flat configs too, but we have to manually construct a
-`DbConfigB Opt` and `ServiceConfigB Opt`:
-
-``` haskell
-configOpt1 :: ConfigB Opt
-configOpt1
-  = ConfigB dbOpt serviceOpt dirOpt
-
-dbOpt :: DbConfigB Opt
-dbOpt
-  = DbConfigB dbHostOpt dbPortOpt
-
-serviceOpt :: ServiceConfigB Opt
-serviceOpt
-  = ServiceConfigB portOpt logOpt
-```
-
-And to run the parser:
-
-``` haskell
-getConfig1 :: IO ()
-getConfig1 = do
-  ConfigB (DbConfigB dbHost dbPort) (ServiceConfigB port log) dir <-
-    execOptDef configOpt1
-  let
-    db      = DbConfig <$> dbHost <*> dbPort
-    service = ServiceConfig <$> port <*> log
-  print $ runIdentity $
-    Config
-    <$> db
-    <*> service
-    <*> dir
-```
-
-### 2. Using `higgledy`
-
-`higgledy` puts an `f` before every type, so doing something like `HKD Config f`
-doesn't make sense: looking at `ConfigB` it seems like the `f` needs to go to
-the right hand side of the nested types. We can, however, avoid the boilerplate
-of defining `barbie` types for the nested datatypes:
-
-``` haskell
-data ConfigH f
-  = ConfigH
-      { _cDbH      :: HKD DbConfig f
-      , _cServiceH :: HKD ServiceConfig f
-      , _cDirH     :: f String
-      }
-  deriving (Generic, B.FunctorB, B.TraversableB, B.ProductB)
-
-configOpt2 :: ConfigH Opt
-configOpt2
-  = ConfigH dbOptH serviceOptH dirOpt
-
-dbOptH :: HKD DbConfig Opt
-dbOptH
-  = build @DbConfig dbHostOpt dbPortOpt
-
-serviceOptH :: HKD ServiceConfig Opt
-serviceOptH
-  = build @ServiceConfig portOpt logOpt
-```
-
-And to run the parser:
-
-``` haskell
-getConfig2 :: IO ()
-getConfig2 = do
-  ConfigH db service dir <- execOptDef configOpt2
-  print $ runIdentity $
-    Config
-    <$> construct db
-    <*> construct service
-    <*> dir
-```
-
-### 2. Using products
-
-Recall from previously that there's the `Single` type which in general turns `f
-b` into `b f`. This means that, by using `Single` for the directory option, all
-`f`s are after their types, so we can just use `:*` instead of having to declare
-a new datatype:
-
-``` haskell
-type ConfigP
-  =  HKD DbConfig
-  :* HKD ServiceConfig
-  :* Single String
-
-configOpt3 :: ConfigP Opt
-configOpt3
-  = dbOptH :* serviceOptH :* single dirOpt
-
-getConfig3 :: IO ()
-getConfig3 = do
-  db :* service :* dir <- execOptDef configOpt3
-  print $ runIdentity $
-    Config
-    <$> construct db
-    <*> construct service
-    <*> getSingle dir
-```
-
-And, to make things look more orthogonal, `harg` defines a type called `Nested`,
-which is exactly the same as `HKD`. There are functions that correspond to
-`build` and `construct`, too:
-
-```
-Nested    <-> HKD
-nested    <-> build
-getNested <-> construct
-```
-
-This means that the previous code block might as well be:
-
-``` haskell
-type ConfigP'
-  =  Nested DbConfig
-  :* Nested ServiceConfig
-  :* Single String
-
-configOpt4 :: ConfigP' Opt
-configOpt4
-  = dbOptN :* serviceOptN :* single dirOpt
-  where
-    dbOptN
-      = nested @DbConfig dbHostOpt dbPortOpt
-    serviceOptN
-      = nested @ServiceConfig portOpt logOpt
-
-getConfig4 :: IO ()
-getConfig4 = do
-  db :* service :* dir <- execOptDef configOpt4
-  print $ runIdentity $
-    Config
-    <$> getNested db
-    <*> getNested service
-    <*> getSingle dir
-```
-
-Pretty cool.
-
-## Subcommands
-
-`harg` also supports (somewhat limited) subcommands, again by using `optparse-applicative`
-underneath.
-
-Because of limitations with higher kinded data when it comes to sum types,
-`harg` uses a different way to define subcommands. `optparse-applicative` allows
-defining subcommands that result to the same type, which means the user needs to
-define a sum type, and each subcommand results in a different constructor. In
-contrast, `harg` defines subcommands that can return completely different types.
-Instead of the result being a sum type, where the user has to pattern match on
-constructors, the result is a `Variant`, which is defined (almost) like this:
-
-``` haskell
-data Variant (xs :: [Type]) where
-  Here :: x -> Variant (x ': xs)
-  There :: Variant xs -> Variant (y ': xs)
-```
-
-`Variant` is like a sum type which holds all the summands in a type-level list.
-Instead of pattern matching in `Left` or `Right` like when using `Either`, we
-pattern match on `Here x`, `There (Here x)` etc. For a pretty thorough
-introduction to `Variant` and more heterogeneous types, check out
-[this repo](https://github.com/i-am-tom/learn-me-a-haskell) by
-[i-am-tom](https://github.com/i-am-tom).
-
-``` haskell
-x :: Variant '[Int, Bool, Char]
-x = There (Here True)
-
-run :: Variant '[Int, Bool, Char] -> Maybe Bool
-run (Here _)                 = Nothing
-run (There (Here b))         = Just b
-run (There (There (Here _))) = Nothing
-
--- > run x
--- Just True
-```
-
-`harg` defines another kind of variant called `VariantF`:
-
-``` haskell ignore
-data VariantF (xs :: [(Type -> Type) -> Type]) (f :: Type -> Type) where
-```
-
-to hold a type-level list of `barbie` types and the `f` to wrap every type with.
-
-To define a type to be used in a subcommand parser we need the target type and
-the subcommand name, which is encoded as a type-level string `Symbol`. There's a
-handy way to define this. Suppose that the the `Config` type above is the
-configuration type when the command is `app` and another type, e.g.`TestConfig`
-is the configuration when the command is `test`:
-
-``` haskell
-data TestConfig
-  = TestConfig
-      { _tcFoo :: String
-      , _tcBar :: Int
-      }
-  deriving Show
-
-fooOpt :: Opt String
-fooOpt
-  = option strParser
-      ( short 'f'
-      . help "Something foo"
-      . defaultVal "this is the default foo"
-      )
-
-barOpt :: Opt Int
-barOpt
-  = option readParser
-      ( short 'b'
-      . help "Something bar"
-      . defaultVal 42
-      )
-
-type TestConfigP
-  = Single String :* Single Int
-
-testConfigOpt :: TestConfigP Opt
-testConfigOpt
-  = single fooOpt :* single barOpt
-```
-
-The subcommand type looks like this:
-
-``` haskell
-type SubcommandConfig
-  =  "app" :-> ConfigP'
-  :+ "test" :-> TestConfigP
-```
-
-The `+` here stands for sum. The associated option type is:
-
-``` haskell
-subcommandOpt :: SubcommandConfig Opt
-subcommandOpt
-  = configOpt4 :+ testConfigOpt :+ ANil
-```
-
-The `ANil` here marks the end of the association list (which is a heterogeneous
-list that associates symbols with types).
-
-Here's how to run this parser:
-
-``` haskell
-getSubcommand :: IO ()
-getSubcommand = do
-  result <- execCommandsDef subcommandOpt
-  case result of
-    HereF (db :* service :* dir)
-      -> print $ runIdentity $
-           Config
-           <$> getNested db
-           <*> getNested service
-           <*> getSingle dir
-    ThereF (HereF (foo :* bar))
-      -> print $ runIdentity $
-           TestConfig
-           <$> getSingle foo
-           <*> getSingle bar
-```
-
-Or use `fromVariantF`, which is similar to the `either` function:
-
-``` haskell
-getSubcommand' :: IO ()
-getSubcommand' = do
-  result <- execCommandsDef subcommandOpt
-  fromVariantF result
-    (\(db :* service :* dir)
-       -> print $ runIdentity $
-            Config
-            <$> getNested db
-            <*> getNested service
-            <*> getSingle dir
-    )
-    (\(foo :* bar)
-       -> print $ runIdentity $
-            TestConfig
-            <$> getSingle foo
-            <*> getSingle bar
-    )
-```
-
-The type of `fromVariantF` can be thought of as being:
-
-``` haskell ignore
-fromVariantF
-  :: VariantF '[a, b, c, ...] f
-  -> (a f -> r)
-  -> (b f -> r)
-  -> (c f -> r)
-  -> ...
-  -> r
-```
-
-The signature will accept the appropriate number of functions depending on the
-length of the type level list.
-
-## More than just environment variables
-
-You may have noticed the use of `execOptDef` and `execCommandsDef` in all of the
-examples up to now. There are actually more configurable versions of these,
-called `execOpt` and `execCommands` respectively. With these functions the user
-can select where to get options from. For example, `execOptDef` is a shorthand
-for `execOpt EnvSource`, which means that options will be fetched from
-environment variables only (along with the command line, which is always
-required, and defaults, which can be optionally provided by the user).
-
-The sources currently supported are environment variables, json and yaml files.
-
-### Configuring using a json file
-
-First of all, let's use `FlatConfig` from the first example:
-
-``` haskell ignore
-data FlatConfig
-  = FlatConfig
-      { _fcDbHost :: String
-      , _fcDbPort :: Int
-      , _fcDir    :: String
-      , _fcLog    :: Bool  -- whether to log or not
-      }
-  deriving (Show, Generic)
-
-dbHostOpt :: Opt String
-dbHostOpt
-  = option strParser
-      ( long "host"
-      . short 'h'
-      . metavar "DB_HOST"
-      . help "The database host"
-      )
-
-dbPortOpt :: Opt Int
-dbPortOpt
-  = option readParser
-      ( long "port"
-      . help "The database port"
-      . envVar "DB_PORT"
-      . defaultVal 5432
-      )
-
-dirOpt :: Opt String
-dirOpt
-  = argument strParser
-      ( help "Some directory"
-      . defaultVal "/home/user/something"
-      )
-
-logOpt :: Opt Bool
-logOpt
-  = switch
-      ( long "log"
-      . help "Whether to log or not"
-      )
-
-flatConfigOpt3 :: HKD FlatConfig Opt
-flatConfigOpt3
-  = build @FlatConfig dbHostOpt dbPortOpt dirOpt logOpt
-```
-
-To use the JSON source, a `FromJSON` instance is required. Thankfully that's
-easy, since `FlatConfig` has `Generic` instance:
-
-``` haskell
-instance FromJSON FlatConfig
-```
-
-In `harg`, sources are defined as products (using `:*`) of options, which means
-that the definition of the sources is not very different than defining options!
-If we only needed the environment variable source, the options would be:
-
-``` haskell ignore
-envSource :: EnvSource Opt
-envSource = EnvSource
-```
-
-There's no need to actually define an option for the environment because there's
-no meaningful configuration for this. To use the `EnvSource` along with a json
-config, we use the following option:
-
-``` haskell
-sourceOpt :: (EnvSource :* JSONSource) Opt
-sourceOpt
-  = EnvSource :* JSONSource jsonOpt
-  where
-    jsonOpt :: Opt ConfigFile
-    jsonOpt
-      = option strParser
-          ( long "json"
-          . short 'j'
-          . help "JSON config filepath"
-          )
-```
-
-Here, the type of the option for the JSON source is `ConfigFile`. This type is a
-wrapper around `FilePath`, which looks like this:
-
-``` haskell ignore
-data ConfigFile
-  = ConfigFile FilePath
-  | NoConfigFile
-```
-
-This has the advantage that, if the user wants to specify an optional
-configuration file, they can simply say:
-
-``` haskell
-jsonOpt :: Opt ConfigFile
-jsonOpt
-  = option strParser
-      ( long "json"
-      . defaultVal NoConfigFile
-      )
-```
+[`higgledy`](https://hackage.haskell.org/package/higgledy) also helps reduce
+boilerplate code significantly.
 
-Also, because `ConfigFile` has an `IsString` instance, there's no need to say
-`long (ConfigFile "json")` (if `OverloadedStrings` is enabled).
+## Documentation
 
-There's a bit of a disconnect between `ConfigFile` and the ability to make
-optional options using `Maybe` and `optional`. The reason for it is that the
-type that `JSONSource` wraps is not polymorphic, since it needs to be a filepath
-specifically.
+To find out more, check out the [docs](https://alexpeits.github.io/harg) or the
+package page on [hackage](https://hackage.haskell.org/package/harg). There is
+also an [example](https://github.com/alexpeits/harg/blob/master/Example.hs)
+module that serves as an extensive demonstration of the library.
 
-# Roadmap
+## Roadmap
 
 - Better errors using `optparse-applicative`'s internals
 - Allow user to pass `optparse-applicative` preferences
 - Write tests
 
-# Credits
+## Credits
 
 - [jcpetruzza](https://github.com/jcpetruzza)
 - [i-am-tom](https://github.com/i-am-tom)
diff --git a/docs/docs.lhs b/docs/docs.lhs
new file mode 100644
--- /dev/null
+++ b/docs/docs.lhs
@@ -0,0 +1,799 @@
+<!-- -*- tab-width: 2; -*- -->
+# harg
+
+`harg` is a library for configuring programs by scanning command line arguments,
+environment variables, default values and more. Under the hood, it uses a subset
+of [`optparse-applicative`](https://hackage.haskell.org/package/optparse-applicative)
+to expose regular arguments, switch arguments and subcommands. The library
+relies heavily on the use of higher kinded data (HKD) thanks to the
+[`barbies`](https://hackage.haskell.org/package/barbies) library. Using
+[`higgledy`](https://hackage.haskell.org/package/higgledy) also helps reduce
+boilerplate code significantly.
+
+The main goal while developing `harg` was to not have to go through the usual
+pattern of manually `mappend`ing the results of command line parsing, env vars
+and defaults.
+
+This file is also a literate haskell file which is checked when tests are run,
+so all examples should compile and work, and be up to date with the latest
+version of the library.
+
+# Usage
+
+Here are some different usage scenarios. Let's first enable some language
+extensions and add some imports:
+
+``` haskell
+{-# LANGUAGE DataKinds          #-}
+{-# LANGUAGE DeriveAnyClass     #-}
+{-# LANGUAGE DeriveGeneric      #-}
+{-# LANGUAGE FlexibleInstances  #-}
+{-# LANGUAGE GADTs              #-}
+{-# LANGUAGE KindSignatures     #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE TypeApplications   #-}
+{-# LANGUAGE TypeOperators      #-}
+
+import           Data.Functor.Identity (Identity (..))
+import           Data.Kind             (Type)
+import           GHC.Generics          (Generic)
+
+import qualified Data.Barbie           as B
+import           Data.Aeson            (FromJSON)
+import           Data.Generic.HKD      (HKD, build, construct)
+
+import           Options.Harg
+
+main :: IO ()
+main = putStrLn "this is a literate haskell file"
+```
+
+## One flat (non-nested) datatype
+
+The easiest scenario is when the target configuration type is one single record
+with no levels of nesting:
+
+``` haskell
+data FlatConfig
+  = FlatConfig
+      { _fcDbHost :: String
+      , _fcDbPort :: Int
+      , _fcDir    :: String
+      , _fcLog    :: Bool  -- whether to log or not
+      }
+  deriving (Show, Generic)
+```
+
+(The `Generic` instance is required for section `3` later on)
+
+Let's first create the `Opt`s for each value in `FlatConfig`. `Opt` is the
+description for each component of the configuration.
+
+``` haskell
+dbHostOpt :: Opt String
+dbHostOpt
+  = option strParser
+      ( long "host"
+      . short 'h'
+      . metavar "DB_HOST"
+      . help "The database host"
+      )
+
+dbPortOpt :: Opt Int
+dbPortOpt
+  = option readParser
+      ( long "port"
+      . help "The database port"
+      . envVar "DB_PORT"
+      . defaultVal 5432
+      )
+
+dirOpt :: Opt String
+dirOpt
+  = argument strParser
+      ( help "Some directory"
+      . defaultVal "/home/user/something"
+      )
+
+logOpt :: Opt Bool
+logOpt
+  = switch
+      ( long "log"
+      . help "Whether to log or not"
+      )
+```
+
+Here, we use `option` to define a command line argument that expects a value
+after it, `argument` to define a standalone argument, not prefixed by a long or
+short indicator, and `switch` to define a boolean command line flag that, if
+present, sets the target value to `True`. The `opt*` functions (here applied
+using `&` to make things look more declarative) modify the option configuration.
+`help` adds help text, `defaultVal` adds a default value, `short` adds a short
+command line option as an alternative to the long one (the string after `option`
+or `switch`), `envVar` sets the associated environment variable and `metavar`
+sets the metavariable to be shown in the help text generated by
+`optparse-applicative`.
+
+The first argument (`strParser` or `readParser`) is the parser for the argument,
+be it from the command line or from an environment variable. The type of this
+function should be `String -> Either String a`, which produces an error message
+or the parsed value. `strParser` is equivalent to `pure` and always succeeds.
+`readParser` requires the type to have a `Read` constraint. In order to use it
+with newtypes that wrap a type that has a `Read` constraint, using the `Functor`
+instance for `Opt` should be sufficient. E.g. for the newtype:
+
+``` haskell
+newtype Port = Port Int
+```
+
+we can define the following option:
+
+``` haskell
+dbPortOpt' :: Opt Port
+dbPortOpt'
+  = Port <$> dbPortOpt
+```
+
+Of course, any user-defined function works as well. In addition, to use a
+function of type `String -> Maybe a` use `parseWith`, which runs the parser and
+in case of failure uses a default error message. For example, `readParser` is
+defined as `parseWith readMaybe`.
+
+Finally, an optional option with type `a` can be specified by setting its type
+to `Maybe a`. The declaration is exactly the same as it would be for `a`, and
+adding `optional` to the modifiers turns turns the parser from `String -> Either
+String a` to `String -> Either String (Maybe a)` but without using the `Read`
+instance for `Maybe`:
+
+``` hs
+someOpt :: Opt (Maybe Int)
+someOpt
+  = option readParser
+      ( long "something"
+      . optional
+      )
+```
+
+Note that `optional` can't be used with `defaultVal`. Using them together raises
+a type error at compile time, to ensure there's no ambiguous behaviour (e.g. the
+order of declaration of modifiers should not influence the resulting option).
+
+There are 3 ways to configure this datatype.
+
+### 1. Using a `barbie` type
+
+`barbie` types are types of kind `(Type -> Type) -> Type`. The `barbie` type for
+`FlatConfig` looks like this:
+
+``` haskell
+data FlatConfigB f
+  = FlatConfigB
+      { _fcDbHostB :: f String
+      , _fcDbPortB :: f Int
+      , _fcDirB    :: f String
+      , _fcLogB    :: f Bool
+      }
+  deriving (Generic, B.FunctorB, B.TraversableB, B.ProductB)
+```
+
+I also derived some required instances that come from the `barbies` package.
+These instances allow us to change the `f` (`bmap` from `FunctorB`) and traverse
+all types in the record producing side effects (`btraverse` from
+`TraversableB`).
+
+Now let's define the value of this datatype, which holds our option
+configuration. The type constructor needed for the options is `Opt`:
+
+``` haskell
+flatConfigOpt1 :: FlatConfigB Opt
+flatConfigOpt1
+  = FlatConfigB dbHostOpt dbPortOpt dirOpt logOpt
+```
+
+Because `dbHostOpt`, `dbPortOpt` and `logOpt` all have type `Opt <actual type>`,
+`flatConfigOpt1` has the correct type according to `FlatConfigB Opt`.
+
+Now to actually run things:
+
+``` haskell
+getFlatConfig1 :: IO ()
+getFlatConfig1 = do
+  FlatConfigB host port dir log <- execOptDef flatConfigOpt1
+  print $ runIdentity $
+    FlatConfig
+    <$> host
+    <*> port
+    <*> dir
+    <*> log
+```
+
+`execOpt` returns an `Identity x` where `x` is the type of the options we are
+configuring, in this case `FlatConfigB`. Here, we pattern match on the
+barbie-type, and then use the `Applicative` instance of `Identity` to get back
+an `Identity FlatConfig`.
+
+This is still a bit boilerplate-y. Let's look at another way.
+
+### 2. Using a product type
+
+Looking at `FlatConfigB`, it's only used because of it's `barbie`-like
+capabilities. Other than that it's just a simple product type with the
+additional `f` before all its sub-types.
+
+`harg` defines a type almost similar to `Product` (from `Data.Functor.Product`),
+which works in a similar fashion as servant's `:<|>` type. This type is defined
+in `Options.Harg.Het.Prod` and is called `:*` (the `*` stands for product). This
+type stores barbie-like types and also keeps the `f` handy: `data (a :* b) f = a
+f :* b f`. This is also easily made an instance of `Generic`, `FunctorB`,
+`TraversableB` and `ProductB`. With all that, let's rewrite the options value
+and the function to get the configuration:
+
+``` haskell
+flatConfigOpt2
+  :: (Single String :* Single Int :* Single String :* Single Bool) Opt
+flatConfigOpt2
+  = single dbHostOpt :* single dbPortOpt :* single dirOpt :* single logOpt
+
+getFlatConfig2 :: IO ()
+getFlatConfig2 = do
+  host :* port :* dir :* log <- execOptDef flatConfigOpt2
+  print $ runIdentity $
+    FlatConfig
+    <$> getSingle host
+    <*> getSingle port
+    <*> getSingle dir
+    <*> getSingle log
+```
+
+This looks aufully similar to the previous version, but without having to write
+another datatype and derive all the instances. `:*` is both a type-level
+constructor and a value-level function that acts like list's `:`. It is also
+right-associative, so for example `a :* b :* c` is the same as `a :* (b :* c)`.
+
+The `Single` type constructor is used when talking about a single value, rather
+than a nested datatype. `Single a f` is a simple newtype over `f a`. The reason
+for using that is simply to switch the order of application, so that we can
+later apply the `f` (here `Opt`) to the compound type (`:*`). This makes type
+definitions look more similar to datatype definitions:
+
+``` haskell
+type FlatConfigOpt2
+  =  Single String
+  :* Single Int
+  :* Single Bool
+```
+
+In addition, `single` is used to wrap an `f a` into a `Single a f`, and
+`getSingle` is used to unwrap it. Later on we'll see how to construct nested
+configurations using `Nested`.
+
+However, the real value when having flat datatypes comes from the ability to use
+`higgledy`.
+
+### 3. Using `HKD` from `higgledy`
+
+``` haskell
+flatConfigOpt3 :: HKD FlatConfig Opt
+flatConfigOpt3
+  = build @FlatConfig dbHostOpt dbPortOpt dirOpt logOpt
+
+getFlatConfig3 :: IO ()
+getFlatConfig3 = do
+  result <- execOptDef flatConfigOpt3
+  print $ runIdentity (construct result)
+```
+
+This is the most straightforward way to work with flat configuration types. The
+`build` function takes as arguments the options (`Opt a` where `a` is each type
+in `FlatConfig`) **in the order they appear in the datatype**, and returns the
+generic representation of a type that's exactly the same as `FlatConfigB`. This
+means that we get all the `barbie` instances for free.
+
+To go back from the `HKD` representation of a datatype to the base one, we use
+`construct`. `construct` uses the applicative instance of the `f` which wraps
+each type in `FlatConfig` to give back an `f FlatConfig` (in our case an
+`Identity FlatConfig`).
+
+## Nested datatypes
+
+Let's say now that we have these two datatypes:
+
+``` haskell
+data DbConfig
+  = DbConfig
+      { _dcHost :: String
+      , _dcPort :: Int
+      }
+  deriving (Show, Generic)
+
+data ServiceConfig
+  = ServiceConfig
+      { _scPort :: Int
+      , _scLog  :: Bool
+      }
+  deriving (Show, Generic)
+```
+
+And the datatype to be configured is this:
+
+``` haskell
+data Config
+  = Config
+      { _cDb      :: DbConfig
+      , _cService :: ServiceConfig
+      , _cDir     :: String
+      }
+  deriving (Show, Generic)
+```
+
+And a new option required for the service port:
+
+``` haskell
+portOpt :: Opt Int
+portOpt
+  = option readParser
+      ( long "port"
+      . help "The service port"
+      . defaultVal 8080
+      )
+```
+
+Again, there are several ways to configure these options.
+
+### 1. Using `barbie` types
+
+Since we now have 3 types, there's a bit more boilerplate to write:
+
+``` haskell
+data ConfigB f
+  = ConfigB
+      { _cDbB      :: DbConfigB f
+      , _cServiceB :: ServiceConfigB f
+      , _cDirB     :: f String
+      }
+  deriving (Generic, B.FunctorB, B.TraversableB, B.ProductB)
+
+data DbConfigB f
+  = DbConfigB
+      { _dcHostB :: f String
+      , _dcPortB :: f Int
+      }
+  deriving (Generic, B.FunctorB, B.TraversableB, B.ProductB)
+
+data ServiceConfigB f
+  = ServiceConfigB
+      { _scPortB :: f Int
+      , _scLogB  :: f Bool
+      }
+  deriving (Generic, B.FunctorB, B.TraversableB, B.ProductB)
+```
+
+To define the option parser, we need option parsers for every type inside it.
+This was true for flat configs too, but we have to manually construct a
+`DbConfigB Opt` and `ServiceConfigB Opt`:
+
+``` haskell
+configOpt1 :: ConfigB Opt
+configOpt1
+  = ConfigB dbOpt serviceOpt dirOpt
+
+dbOpt :: DbConfigB Opt
+dbOpt
+  = DbConfigB dbHostOpt dbPortOpt
+
+serviceOpt :: ServiceConfigB Opt
+serviceOpt
+  = ServiceConfigB portOpt logOpt
+```
+
+And to run the parser:
+
+``` haskell
+getConfig1 :: IO ()
+getConfig1 = do
+  ConfigB (DbConfigB dbHost dbPort) (ServiceConfigB port log) dir <-
+    execOptDef configOpt1
+  let
+    db      = DbConfig <$> dbHost <*> dbPort
+    service = ServiceConfig <$> port <*> log
+  print $ runIdentity $
+    Config
+    <$> db
+    <*> service
+    <*> dir
+```
+
+### 2. Using `higgledy`
+
+`higgledy` puts an `f` before every type, so doing something like `HKD Config f`
+doesn't make sense: looking at `ConfigB` it seems like the `f` needs to go to
+the right hand side of the nested types. We can, however, avoid the boilerplate
+of defining `barbie` types for the nested datatypes:
+
+``` haskell
+data ConfigH f
+  = ConfigH
+      { _cDbH      :: HKD DbConfig f
+      , _cServiceH :: HKD ServiceConfig f
+      , _cDirH     :: f String
+      }
+  deriving (Generic, B.FunctorB, B.TraversableB, B.ProductB)
+
+configOpt2 :: ConfigH Opt
+configOpt2
+  = ConfigH dbOptH serviceOptH dirOpt
+
+dbOptH :: HKD DbConfig Opt
+dbOptH
+  = build @DbConfig dbHostOpt dbPortOpt
+
+serviceOptH :: HKD ServiceConfig Opt
+serviceOptH
+  = build @ServiceConfig portOpt logOpt
+```
+
+And to run the parser:
+
+``` haskell
+getConfig2 :: IO ()
+getConfig2 = do
+  ConfigH db service dir <- execOptDef configOpt2
+  print $ runIdentity $
+    Config
+    <$> construct db
+    <*> construct service
+    <*> dir
+```
+
+### 2. Using products
+
+Recall from previously that there's the `Single` type which in general turns `f
+b` into `b f`. This means that, by using `Single` for the directory option, all
+`f`s are after their types, so we can just use `:*` instead of having to declare
+a new datatype:
+
+``` haskell
+type ConfigP
+  =  HKD DbConfig
+  :* HKD ServiceConfig
+  :* Single String
+
+configOpt3 :: ConfigP Opt
+configOpt3
+  = dbOptH :* serviceOptH :* single dirOpt
+
+getConfig3 :: IO ()
+getConfig3 = do
+  db :* service :* dir <- execOptDef configOpt3
+  print $ runIdentity $
+    Config
+    <$> construct db
+    <*> construct service
+    <*> getSingle dir
+```
+
+And, to make things look more orthogonal, `harg` defines a type called `Nested`,
+which is exactly the same as `HKD`. There are functions that correspond to
+`build` and `construct`, too:
+
+```
+Nested    <-> HKD
+nested    <-> build
+getNested <-> construct
+```
+
+This means that the previous code block might as well be:
+
+``` haskell
+type ConfigP'
+  =  Nested DbConfig
+  :* Nested ServiceConfig
+  :* Single String
+
+configOpt4 :: ConfigP' Opt
+configOpt4
+  = dbOptN :* serviceOptN :* single dirOpt
+  where
+    dbOptN
+      = nested @DbConfig dbHostOpt dbPortOpt
+    serviceOptN
+      = nested @ServiceConfig portOpt logOpt
+
+getConfig4 :: IO ()
+getConfig4 = do
+  db :* service :* dir <- execOptDef configOpt4
+  print $ runIdentity $
+    Config
+    <$> getNested db
+    <*> getNested service
+    <*> getSingle dir
+```
+
+Pretty cool.
+
+## Subcommands
+
+`harg` also supports (somewhat limited) subcommands, again by using `optparse-applicative`
+underneath.
+
+Because of limitations with higher kinded data when it comes to sum types,
+`harg` uses a different way to define subcommands. `optparse-applicative` allows
+defining subcommands that result to the same type, which means the user needs to
+define a sum type, and each subcommand results in a different constructor. In
+contrast, `harg` defines subcommands that can return completely different types.
+Instead of the result being a sum type, where the user has to pattern match on
+constructors, the result is a `Variant`, which is defined (almost) like this:
+
+``` haskell
+data Variant (xs :: [Type]) where
+  Here :: x -> Variant (x ': xs)
+  There :: Variant xs -> Variant (y ': xs)
+```
+
+`Variant` is like a sum type which holds all the summands in a type-level list.
+Instead of pattern matching in `Left` or `Right` like when using `Either`, we
+pattern match on `Here x`, `There (Here x)` etc. For a pretty thorough
+introduction to `Variant` and more heterogeneous types, check out
+[this repo](https://github.com/i-am-tom/learn-me-a-haskell) by
+[i-am-tom](https://github.com/i-am-tom).
+
+``` haskell
+x :: Variant '[Int, Bool, Char]
+x = There (Here True)
+
+run :: Variant '[Int, Bool, Char] -> Maybe Bool
+run (Here _)                 = Nothing
+run (There (Here b))         = Just b
+run (There (There (Here _))) = Nothing
+
+-- > run x
+-- Just True
+```
+
+`harg` defines another kind of variant called `VariantF`:
+
+``` hs
+data VariantF (xs :: [(Type -> Type) -> Type]) (f :: Type -> Type) where
+```
+
+to hold a type-level list of `barbie` types and the `f` to wrap every type with.
+
+To define a type to be used in a subcommand parser we need the target type and
+the subcommand name, which is encoded as a type-level string `Symbol`. There's a
+handy way to define this. Suppose that the the `Config` type above is the
+configuration type when the command is `app` and another type, e.g.`TestConfig`
+is the configuration when the command is `test`:
+
+``` haskell
+data TestConfig
+  = TestConfig
+      { _tcFoo :: String
+      , _tcBar :: Int
+      }
+  deriving Show
+
+fooOpt :: Opt String
+fooOpt
+  = option strParser
+      ( short 'f'
+      . help "Something foo"
+      . defaultVal "this is the default foo"
+      )
+
+barOpt :: Opt Int
+barOpt
+  = option readParser
+      ( short 'b'
+      . help "Something bar"
+      . defaultVal 42
+      )
+
+type TestConfigP
+  = Single String :* Single Int
+
+testConfigOpt :: TestConfigP Opt
+testConfigOpt
+  = single fooOpt :* single barOpt
+```
+
+The subcommand type looks like this:
+
+``` haskell
+type SubcommandConfig
+  =  "app" :-> ConfigP'
+  :+ "test" :-> TestConfigP
+```
+
+The `+` here stands for sum. The associated option type is:
+
+``` haskell
+subcommandOpt :: SubcommandConfig Opt
+subcommandOpt
+  = configOpt4 :+ testConfigOpt :+ ANil
+```
+
+The `ANil` here marks the end of the association list (which is a heterogeneous
+list that associates symbols with types).
+
+Here's how to run this parser:
+
+``` haskell
+getSubcommand :: IO ()
+getSubcommand = do
+  result <- execCommandsDef subcommandOpt
+  case result of
+    HereF (db :* service :* dir)
+      -> print $ runIdentity $
+           Config
+           <$> getNested db
+           <*> getNested service
+           <*> getSingle dir
+    ThereF (HereF (foo :* bar))
+      -> print $ runIdentity $
+           TestConfig
+           <$> getSingle foo
+           <*> getSingle bar
+```
+
+Or use `fromVariantF`, which is similar to the `either` function:
+
+``` haskell
+getSubcommand' :: IO ()
+getSubcommand' = do
+  result <- execCommandsDef subcommandOpt
+  fromVariantF result
+    (\(db :* service :* dir)
+       -> print $ runIdentity $
+            Config
+            <$> getNested db
+            <*> getNested service
+            <*> getSingle dir
+    )
+    (\(foo :* bar)
+       -> print $ runIdentity $
+            TestConfig
+            <$> getSingle foo
+            <*> getSingle bar
+    )
+```
+
+The type of `fromVariantF` can be thought of as being:
+
+``` hs
+fromVariantF
+  :: VariantF '[a, b, c, ...] f
+  -> (a f -> r)
+  -> (b f -> r)
+  -> (c f -> r)
+  -> ...
+  -> r
+```
+
+The signature will accept the appropriate number of functions depending on the
+length of the type level list.
+
+## More than just environment variables
+
+You may have noticed the use of `execOptDef` and `execCommandsDef` in all of the
+examples up to now. There are actually more configurable versions of these,
+called `execOpt` and `execCommands` respectively. With these functions the user
+can select where to get options from. For example, `execOptDef` is a shorthand
+for `execOpt EnvSource`, which means that options will be fetched from
+environment variables only (along with the command line, which is always
+required, and defaults, which can be optionally provided by the user).
+
+The sources currently supported are environment variables, json and yaml files.
+
+### Configuring using a json file
+
+First of all, let's use `FlatConfig` from the first example:
+
+``` hs
+data FlatConfig
+  = FlatConfig
+      { _fcDbHost :: String
+      , _fcDbPort :: Int
+      , _fcDir    :: String
+      , _fcLog    :: Bool  -- whether to log or not
+      }
+  deriving (Show, Generic)
+
+dbHostOpt :: Opt String
+dbHostOpt
+  = option strParser
+      ( long "host"
+      . short 'h'
+      . metavar "DB_HOST"
+      . help "The database host"
+      )
+
+dbPortOpt :: Opt Int
+dbPortOpt
+  = option readParser
+      ( long "port"
+      . help "The database port"
+      . envVar "DB_PORT"
+      . defaultVal 5432
+      )
+
+dirOpt :: Opt String
+dirOpt
+  = argument strParser
+      ( help "Some directory"
+      . defaultVal "/home/user/something"
+      )
+
+logOpt :: Opt Bool
+logOpt
+  = switch
+      ( long "log"
+      . help "Whether to log or not"
+      )
+
+flatConfigOpt3 :: HKD FlatConfig Opt
+flatConfigOpt3
+  = build @FlatConfig dbHostOpt dbPortOpt dirOpt logOpt
+```
+
+To use the JSON source, a `FromJSON` instance is required. Thankfully that's
+easy, since `FlatConfig` has `Generic` instance:
+
+``` haskell
+instance FromJSON FlatConfig
+```
+
+In `harg`, sources are defined as products (using `:*`) of options, which means
+that the definition of the sources is not very different than defining options!
+If we only needed the environment variable source, the options would be:
+
+``` hs
+envSource :: EnvSource Opt
+envSource = EnvSource
+```
+
+There's no need to actually define an option for the environment because there's
+no meaningful configuration for this. To use the `EnvSource` along with a json
+config, we use the following option:
+
+``` haskell
+sourceOpt :: (EnvSource :* JSONSource) Opt
+sourceOpt
+  = EnvSource :* JSONSource jsonOpt
+  where
+    jsonOpt :: Opt ConfigFile
+    jsonOpt
+      = option strParser
+          ( long "json"
+          . short 'j'
+          . help "JSON config filepath"
+          )
+```
+
+Here, the type of the option for the JSON source is `ConfigFile`. This type is a
+wrapper around `FilePath`, which looks like this:
+
+``` hs
+data ConfigFile
+  = ConfigFile FilePath
+  | NoConfigFile
+```
+
+This has the advantage that, if the user wants to specify an optional
+configuration file, they can simply say:
+
+``` haskell
+jsonOpt :: Opt ConfigFile
+jsonOpt
+  = option strParser
+      ( long "json"
+      . defaultVal NoConfigFile
+      )
+```
+
+Also, because `ConfigFile` has an `IsString` instance, there's no need to say
+`long (ConfigFile "json")` (if `OverloadedStrings` is enabled).
+
+There's a bit of a disconnect between `ConfigFile` and the ability to make
+optional options using `Maybe` and `optional`. The reason for it is that the
+type that `JSONSource` wraps is not polymorphic, since it needs to be a filepath
+specifically.
diff --git a/harg.cabal b/harg.cabal
--- a/harg.cabal
+++ b/harg.cabal
@@ -1,7 +1,7 @@
 cabal-version: 2.2
 
 name:           harg
-version:        0.4.1.0
+version:        0.4.2.0
 synopsis:       Haskell program configuration using higher kinded data
 description:    Please see the README on GitHub at <https://github.com/alexpeits/harg#readme>
 homepage:       https://github.com/alexpeits/harg
@@ -86,9 +86,10 @@
                   , harg
   default-language: Haskell2010
 
-test-suite readme-test
+test-suite docs-test
   type:               exitcode-stdio-1.0
-  main-is:            README.lhs
+  hs-source-dirs:     docs
+  main-is:            docs.lhs
   ghc-options:        -Wall
                       -Wno-unticked-promoted-constructors
                       -threaded
diff --git a/src/Options/Harg.hs b/src/Options/Harg.hs
--- a/src/Options/Harg.hs
+++ b/src/Options/Harg.hs
@@ -15,8 +15,8 @@
   , fromSingle
 
   , Nested (..)
-  , nested
   , getNested
+  , nested
   , fromNested
 
   , AssocListF (..)
@@ -37,7 +37,6 @@
   , defaultStr
   , required
   , optional
-  , toOpt
   , Opt
 
   -- ** Option parsers
@@ -50,8 +49,12 @@
   -- ** Executing options
   , execOpt
   , execOptDef
+  , execOptWithCtx
+  , execOptWithCtxDef
   , execCommands
   , execCommandsDef
+  , execCommandsWithCtx
+  , execCommandsWithCtxDef
 
   -- ** Option sources
   , EnvSource (..)
diff --git a/src/Options/Harg/Cmdline.hs b/src/Options/Harg/Cmdline.hs
--- a/src/Options/Harg/Cmdline.hs
+++ b/src/Options/Harg/Cmdline.hs
@@ -1,4 +1,6 @@
-module Options.Harg.Cmdline where
+module Options.Harg.Cmdline
+  ( mkOptparseParser
+  ) where
 
 import           Control.Applicative  ((<|>))
 import           Data.Functor.Compose (Compose (..))
@@ -8,7 +10,7 @@
 import qualified Data.Barbie          as B
 import qualified Options.Applicative  as Optparse
 
-import           Options.Harg.Pretty
+import           Options.Harg.Pretty  (ppHelp)
 import           Options.Harg.Types
 
 
diff --git a/src/Options/Harg/Config.hs b/src/Options/Harg/Config.hs
--- a/src/Options/Harg/Config.hs
+++ b/src/Options/Harg/Config.hs
@@ -1,4 +1,7 @@
-module Options.Harg.Config where
+module Options.Harg.Config
+  ( mkConfigParser
+  , getConfig
+  ) where
 
 import           Data.Functor.Compose       (Compose (..))
 import           Data.Kind                  (Type)
@@ -6,9 +9,9 @@
 import qualified Data.Barbie                as B
 import qualified Options.Applicative        as Optparse
 
-import           Options.Harg.Cmdline
-import           Options.Harg.Sources
-import           Options.Harg.Sources.Env
+import           Options.Harg.Cmdline       (mkOptparseParser)
+import           Options.Harg.Sources       (accumSourceResults)
+import           Options.Harg.Sources.Env   (EnvSourceVal(..))
 import           Options.Harg.Sources.Types
 import           Options.Harg.Types
 
diff --git a/src/Options/Harg/Construct.hs b/src/Options/Harg/Construct.hs
--- a/src/Options/Harg/Construct.hs
+++ b/src/Options/Harg/Construct.hs
@@ -2,7 +2,27 @@
 {-# LANGUAGE TypeFamilies            #-}
 {-# LANGUAGE UndecidableInstances    #-}
 {-# LANGUAGE UndecidableSuperClasses #-}
-module Options.Harg.Construct where
+module Options.Harg.Construct
+  ( option
+  , flag
+  , switch
+  , switch'
+  , argument
+  , long
+  , short
+  , help
+  , metavar
+  , envVar
+  , defaultVal
+  , defaultStr
+  , required
+  , optional
+  , parseWith
+  , readParser
+  , strParser
+  , boolParser
+  , manyParser
+  ) where
 
 import Data.Char          (toLower)
 import Data.Kind          (Constraint)
diff --git a/src/Options/Harg/Het/All.hs b/src/Options/Harg/Het/All.hs
--- a/src/Options/Harg/Het/All.hs
+++ b/src/Options/Harg/Het/All.hs
@@ -1,6 +1,8 @@
+{-# LANGUAGE PolyKinds    #-}
 {-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE PolyKinds #-}
-module Options.Harg.Het.All where
+module Options.Harg.Het.All
+  ( All
+  ) where
 
 import Data.Kind (Type, Constraint)
 
diff --git a/src/Options/Harg/Het/HList.hs b/src/Options/Harg/Het/HList.hs
--- a/src/Options/Harg/Het/HList.hs
+++ b/src/Options/Harg/Het/HList.hs
@@ -2,7 +2,13 @@
 {-# LANGUAGE RankNTypes           #-}
 {-# LANGUAGE TypeFamilies         #-}
 {-# LANGUAGE UndecidableInstances #-}
-module Options.Harg.Het.HList where
+module Options.Harg.Het.HList
+  ( MapAssocList (..)
+  , AssocListF (..)
+  , (:+)
+  , pattern (:+)
+  , (:->)
+  ) where
 
 import           Data.Kind    (Type)
 import           GHC.TypeLits (ErrorMessage(..), TypeError, Symbol)
diff --git a/src/Options/Harg/Het/Nat.hs b/src/Options/Harg/Het/Nat.hs
--- a/src/Options/Harg/Het/Nat.hs
+++ b/src/Options/Harg/Het/Nat.hs
@@ -1,4 +1,7 @@
-module Options.Harg.Het.Nat where
+module Options.Harg.Het.Nat
+  ( Nat (..)
+  , SNat (..)
+  ) where
 
 -- | Type-level Peano natural number.
 data Nat
diff --git a/src/Options/Harg/Het/Prod.hs b/src/Options/Harg/Het/Prod.hs
--- a/src/Options/Harg/Het/Prod.hs
+++ b/src/Options/Harg/Het/Prod.hs
@@ -5,7 +5,10 @@
 {-# LANGUAGE PolyKinds                  #-}
 {-# LANGUAGE StandaloneDeriving         #-}
 {-# LANGUAGE UndecidableInstances       #-}
-module Options.Harg.Het.Prod where
+module Options.Harg.Het.Prod
+  ( (:*) (..)
+  , Tagged (..)
+  ) where
 
 import           Data.Functor.Identity (Identity)
 import           Data.Kind             (Type)
diff --git a/src/Options/Harg/Het/Proofs.hs b/src/Options/Harg/Het/Proofs.hs
--- a/src/Options/Harg/Het/Proofs.hs
+++ b/src/Options/Harg/Het/Proofs.hs
@@ -6,7 +6,11 @@
 {-# LANGUAGE RankNTypes           #-}
 {-# LANGUAGE TypeFamilies         #-}
 {-# LANGUAGE UndecidableInstances #-}
-module Options.Harg.Het.Proofs where
+module Options.Harg.Het.Proofs
+  ( type (++)
+  , Proof (..)
+  , hgcastWith
+  ) where
 
 import Data.Kind          (Type)
 import Data.Type.Equality
diff --git a/src/Options/Harg/Het/Variant.hs b/src/Options/Harg/Het/Variant.hs
--- a/src/Options/Harg/Het/Variant.hs
+++ b/src/Options/Harg/Het/Variant.hs
@@ -3,7 +3,16 @@
 {-# LANGUAGE PatternSynonyms        #-}
 {-# LANGUAGE TypeFamilies           #-}
 {-# LANGUAGE UndecidableInstances   #-}
-module Options.Harg.Het.Variant where
+module Options.Harg.Het.Variant
+  ( VariantF (..)
+  , fromVariantF
+  , InjectPosF (..)
+  , pattern In1
+  , pattern In2
+  , pattern In3
+  , pattern In4
+  , pattern In5
+  ) where
 
 import           Data.Kind            (Type)
 
diff --git a/src/Options/Harg/Nested.hs b/src/Options/Harg/Nested.hs
--- a/src/Options/Harg/Nested.hs
+++ b/src/Options/Harg/Nested.hs
@@ -7,7 +7,12 @@
 {-# LANGUAGE UndecidableInstances       #-}
 
 {-# OPTIONS_GHC -fno-warn-orphans #-}
-module Options.Harg.Nested where
+module Options.Harg.Nested
+  ( Nested (..)
+  , nested
+  , getNested
+  , fromNested
+  ) where
 
 import           Data.Coerce           (Coercible, coerce)
 import           Data.Functor.Identity (Identity(..))
diff --git a/src/Options/Harg/Operations.hs b/src/Options/Harg/Operations.hs
--- a/src/Options/Harg/Operations.hs
+++ b/src/Options/Harg/Operations.hs
@@ -4,28 +4,37 @@
 {-# LANGUAGE RankNTypes           #-}
 {-# LANGUAGE TypeFamilies         #-}
 {-# LANGUAGE UndecidableInstances #-}
-module Options.Harg.Operations where
+module Options.Harg.Operations
+  ( execOpt
+  , execOptDef
+  , execOptWithCtx
+  , execOptWithCtxDef
+  , execCommands
+  , execCommandsDef
+  , execCommandsWithCtx
+  , execCommandsWithCtxDef
+  ) where
 
-import           Data.Functor.Identity           (Identity(..))
+import           Data.Functor.Identity      (Identity(..))
 
-import qualified Data.Barbie                     as B
-import qualified Options.Applicative             as Optparse
+import qualified Data.Barbie                as B
+import qualified Options.Applicative        as Optparse
 
-import           Options.Harg.Cmdline            (mkOptparseParser)
-import           Options.Harg.Config             (mkConfigParser, getConfig)
-import           Options.Harg.Het.All            (All)
-import           Options.Harg.Het.HList          (AssocListF, MapAssocList(..))
-import           Options.Harg.Het.Prod           ((:*)(..))
-import           Options.Harg.Het.Variant        (VariantF)
-import           Options.Harg.Pretty             (ppSourceRunErrors)
-import           Options.Harg.Sources            ( accumSourceResults
-                                                 , DefaultSources, defaultSources
-                                                 , HiddenSources, hiddenSources
-                                                 )
-import           Options.Harg.Sources.Types      (GetSource(..), RunSource(..), SourceRunError)
-import           Options.Harg.Subcommands        (Subcommands(..))
-import           Options.Harg.Types              (HargCtx(..), getCtx, Opt)
-import           Options.Harg.Util               (toDummyOpts, allToDummyOpts, compose)
+import           Options.Harg.Cmdline       (mkOptparseParser)
+import           Options.Harg.Config        (mkConfigParser, getConfig)
+import           Options.Harg.Het.All       (All)
+import           Options.Harg.Het.HList     (AssocListF, MapAssocList(..))
+import           Options.Harg.Het.Prod      ((:*)(..))
+import           Options.Harg.Het.Variant   (VariantF)
+import           Options.Harg.Pretty        (ppSourceRunErrors)
+import           Options.Harg.Sources       ( accumSourceResults
+                                            , DefaultSources, defaultSources
+                                            , HiddenSources, hiddenSources
+                                            )
+import           Options.Harg.Sources.Types (GetSource(..), RunSource(..), SourceRunError)
+import           Options.Harg.Subcommands   (Subcommands(..))
+import           Options.Harg.Types         (HargCtx(..), getCtx, Opt)
+import           Options.Harg.Util          (toDummyOpts, allToDummyOpts, compose)
 
 -- | Run the option parser and combine with values from the specified sources,
 -- passing the context explicitly.
diff --git a/src/Options/Harg/Pretty.hs b/src/Options/Harg/Pretty.hs
--- a/src/Options/Harg/Pretty.hs
+++ b/src/Options/Harg/Pretty.hs
@@ -1,4 +1,7 @@
-module Options.Harg.Pretty where
+module Options.Harg.Pretty
+  ( ppHelp
+  , ppSourceRunErrors
+  ) where
 
 import Control.Applicative        ((<|>))
 import Data.List                  (intercalate)
diff --git a/src/Options/Harg/Single.hs b/src/Options/Harg/Single.hs
--- a/src/Options/Harg/Single.hs
+++ b/src/Options/Harg/Single.hs
@@ -3,7 +3,11 @@
 {-# LANGUAGE PolyKinds                  #-}
 {-# LANGUAGE StandaloneDeriving         #-}
 {-# LANGUAGE UndecidableInstances       #-}
-module Options.Harg.Single where
+module Options.Harg.Single
+  ( Single (..)
+  , single
+  , fromSingle
+  ) where
 
 import           Data.Functor.Identity (Identity(..))
 import qualified Data.Functor.Product  as P
@@ -11,7 +15,6 @@
 import           GHC.Generics          (Generic)
 
 import qualified Data.Aeson            as JSON
-
 import qualified Data.Barbie           as B
 
 
diff --git a/src/Options/Harg/Sources.hs b/src/Options/Harg/Sources.hs
--- a/src/Options/Harg/Sources.hs
+++ b/src/Options/Harg/Sources.hs
@@ -1,12 +1,18 @@
-module Options.Harg.Sources where
+module Options.Harg.Sources
+  ( accumSourceResults
+  , HiddenSources
+  , hiddenSources
+  , DefaultSources
+  , defaultSources
+  ) where
 
-import           Data.Foldable              (foldr')
-import           Data.Functor.Compose       (Compose (..))
+import           Data.Foldable                   (foldr')
+import           Data.Functor.Compose            (Compose(..))
 
-import qualified Data.Barbie                as B
+import qualified Data.Barbie                     as B
 
-import           Options.Harg.Sources.DefaultStr
-import           Options.Harg.Sources.Env
+import           Options.Harg.Sources.DefaultStr (DefaultStrSource(..))
+import           Options.Harg.Sources.Env        (EnvSource(..))
 import           Options.Harg.Sources.Types
 
 
@@ -44,12 +50,10 @@
 
 -- | Sources hidden from user that are always enabled
 hiddenSources :: HiddenSources f
-hiddenSources
-  = DefaultStrSource
+hiddenSources = DefaultStrSource
 
 type DefaultSources = EnvSource
 
 -- | Default sources, equivalent to 'EnvSource'
 defaultSources :: DefaultSources f
-defaultSources
-  = EnvSource
+defaultSources = EnvSource
diff --git a/src/Options/Harg/Sources/DefaultStr.hs b/src/Options/Harg/Sources/DefaultStr.hs
--- a/src/Options/Harg/Sources/DefaultStr.hs
+++ b/src/Options/Harg/Sources/DefaultStr.hs
@@ -1,7 +1,9 @@
 {-# LANGUAGE DeriveAnyClass #-}
 {-# LANGUAGE DeriveGeneric  #-}
 {-# LANGUAGE TypeFamilies   #-}
-module Options.Harg.Sources.DefaultStr where
+module Options.Harg.Sources.DefaultStr
+  ( DefaultStrSource (..)
+  ) where
 
 import           Data.Functor.Compose       (Compose (..))
 import           Data.Kind                  (Type)
diff --git a/src/Options/Harg/Sources/Env.hs b/src/Options/Harg/Sources/Env.hs
--- a/src/Options/Harg/Sources/Env.hs
+++ b/src/Options/Harg/Sources/Env.hs
@@ -1,7 +1,10 @@
 {-# LANGUAGE DeriveAnyClass #-}
 {-# LANGUAGE DeriveGeneric  #-}
 {-# LANGUAGE TypeFamilies   #-}
-module Options.Harg.Sources.Env where
+module Options.Harg.Sources.Env
+  ( EnvSource (..)
+  , EnvSourceVal (..)
+  ) where
 
 import           Data.Functor.Compose       (Compose (..))
 import           Data.Kind                  (Type)
diff --git a/src/Options/Harg/Sources/JSON.hs b/src/Options/Harg/Sources/JSON.hs
--- a/src/Options/Harg/Sources/JSON.hs
+++ b/src/Options/Harg/Sources/JSON.hs
@@ -2,19 +2,21 @@
 {-# LANGUAGE DeriveGeneric        #-}
 {-# LANGUAGE TypeFamilies         #-}
 {-# LANGUAGE UndecidableInstances #-}
-module Options.Harg.Sources.JSON where
+module Options.Harg.Sources.JSON
+  ( JSONSource (..)
+  ) where
 
+import qualified Data.ByteString.Lazy       as LBS
 import           Data.Functor.Compose       (Compose (..))
 import           Data.Functor.Identity      (Identity(..))
 import           GHC.Generics               (Generic)
-import qualified Data.ByteString.Lazy       as LBS
 
 import qualified Data.Aeson                 as JSON
 import qualified Data.Barbie                as B
 
 import           Options.Harg.Sources.Types
 import           Options.Harg.Types
-import           Options.Harg.Util
+import           Options.Harg.Util          (readFileLBS)
 
 -- | Source that enables a parser to read options from a JSON file.
 newtype JSONSource f = JSONSource (f ConfigFile)
diff --git a/src/Options/Harg/Sources/NoSource.hs b/src/Options/Harg/Sources/NoSource.hs
--- a/src/Options/Harg/Sources/NoSource.hs
+++ b/src/Options/Harg/Sources/NoSource.hs
@@ -1,7 +1,10 @@
 {-# LANGUAGE DeriveAnyClass #-}
 {-# LANGUAGE DeriveGeneric  #-}
 {-# LANGUAGE TypeFamilies   #-}
-module Options.Harg.Sources.NoSource where
+module Options.Harg.Sources.NoSource
+  ( NoSource
+  , noSources
+  ) where
 
 import           Data.Kind                  (Type)
 import           GHC.Generics               (Generic)
@@ -21,5 +24,4 @@
 
 -- | Shorthand for writing 'NoSource'.
 noSources :: NoSource f
-noSources
-  = NoSource
+noSources = NoSource
diff --git a/src/Options/Harg/Sources/Types.hs b/src/Options/Harg/Sources/Types.hs
--- a/src/Options/Harg/Sources/Types.hs
+++ b/src/Options/Harg/Sources/Types.hs
@@ -1,13 +1,20 @@
 {-# LANGUAGE DeriveFunctor        #-}
 {-# LANGUAGE TypeFamilies         #-}
 {-# LANGUAGE UndecidableInstances #-}
-module Options.Harg.Sources.Types where
+module Options.Harg.Sources.Types
+  ( GetSource (..)
+  , RunSource (..)
+  , ConfigFile (..)
+  , SourceRunResult (..)
+  , SourceRunError (..)
+  , sourceRunError
+  ) where
 
-import Data.Functor.Compose  (Compose (..))
+import Data.Functor.Compose  (Compose(..))
 import Data.Kind             (Type)
 import Data.String           (IsString(..))
 
-import Options.Harg.Het.Prod
+import Options.Harg.Het.Prod ((:*)(..))
 import Options.Harg.Types
 
 
diff --git a/src/Options/Harg/Sources/YAML.hs b/src/Options/Harg/Sources/YAML.hs
--- a/src/Options/Harg/Sources/YAML.hs
+++ b/src/Options/Harg/Sources/YAML.hs
@@ -2,7 +2,9 @@
 {-# LANGUAGE DeriveGeneric        #-}
 {-# LANGUAGE TypeFamilies         #-}
 {-# LANGUAGE UndecidableInstances #-}
-module Options.Harg.Sources.YAML where
+module Options.Harg.Sources.YAML
+  ( YAMLSource (..)
+  ) where
 
 import           Control.Exception          (displayException)
 import qualified Data.ByteString            as BS
@@ -15,7 +17,7 @@
 
 import           Options.Harg.Sources.Types
 import           Options.Harg.Types
-import           Options.Harg.Util
+import           Options.Harg.Util          (readFileBS)
 
 
 -- | Source that enables a parser to read options from a YAML file.
diff --git a/src/Options/Harg/Subcommands.hs b/src/Options/Harg/Subcommands.hs
--- a/src/Options/Harg/Subcommands.hs
+++ b/src/Options/Harg/Subcommands.hs
@@ -1,6 +1,8 @@
 {-# LANGUAGE AllowAmbiguousTypes  #-}
 {-# LANGUAGE UndecidableInstances #-}
-module Options.Harg.Subcommands where
+module Options.Harg.Subcommands
+  ( Subcommands (..)
+  ) where
 
 import           Data.Functor.Compose       (Compose (..))
 import           Data.Kind                  (Type)
@@ -10,13 +12,13 @@
 import qualified Data.Barbie                as B
 import qualified Options.Applicative        as Optparse
 
-import           Options.Harg.Cmdline
-import           Options.Harg.Het.All
-import           Options.Harg.Het.HList
+import           Options.Harg.Cmdline       (mkOptparseParser)
+import           Options.Harg.Het.All       (All)
+import           Options.Harg.Het.HList     (AssocListF(..))
 import           Options.Harg.Het.Nat
-import           Options.Harg.Het.Proofs
-import           Options.Harg.Het.Variant
-import           Options.Harg.Sources
+import           Options.Harg.Het.Proofs    (type (++), Proof(..), hgcastWith)
+import           Options.Harg.Het.Variant   (VariantF, InjectPosF(..))
+import           Options.Harg.Sources       (accumSourceResults)
 import           Options.Harg.Sources.Types
 import           Options.Harg.Types
 
diff --git a/src/Options/Harg/Types.hs b/src/Options/Harg/Types.hs
--- a/src/Options/Harg/Types.hs
+++ b/src/Options/Harg/Types.hs
@@ -1,5 +1,20 @@
 {-# LANGUAGE DeriveFunctor #-}
-module Options.Harg.Types where
+module Options.Harg.Types
+  ( Opt (..)
+  , OptionOpt (..)
+  , FlagOpt (..)
+  , ArgumentOpt (..)
+  , OptAttr (..)
+  , OptType (..)
+  , SomeOpt (..)
+  , OptReader
+  , HargCtx (..)
+  , Environment
+  , getCtx
+  , ctxFromArgs
+  , ctxFromEnv
+  , pureCtx
+  ) where
 
 import System.Environment (getArgs, getEnvironment)
 
diff --git a/src/Options/Harg/Util.hs b/src/Options/Harg/Util.hs
--- a/src/Options/Harg/Util.hs
+++ b/src/Options/Harg/Util.hs
@@ -1,5 +1,11 @@
 {-# LANGUAGE RankNTypes #-}
-module Options.Harg.Util where
+module Options.Harg.Util
+  ( toDummyOpts
+  , allToDummyOpts
+  , compose
+  , readFileBS
+  , readFileLBS
+  ) where
 
 import qualified Control.Exception          as Exc
 import qualified Data.ByteString            as BS
@@ -11,7 +17,7 @@
 
 import qualified Data.Barbie                as B
 
-import           Options.Harg.Het.HList
+import           Options.Harg.Het.HList     (AssocListF, MapAssocList(..))
 import           Options.Harg.Types
 
 
