packages feed

harg 0.4.0.0 → 0.4.1.0

raw patch · 17 files changed

+562/−425 lines, 17 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Options.Harg.Pretty: ppOptErrors :: [OptError] -> String
- Options.Harg.Sources.DefaultStr: instance Data.Barbie.Internal.Functor.FunctorB a => Options.Harg.Sources.Types.RunSource Options.Harg.Sources.DefaultStr.DefaultStrSourceVal a
- Options.Harg.Sources.Env: instance Data.Barbie.Internal.Functor.FunctorB a => Options.Harg.Sources.Types.RunSource Options.Harg.Sources.Env.EnvSourceVal a
- Options.Harg.Sources.Types: OptFoundNoParse :: OptError -> SourceRunResult a
- Options.Harg.Types: OptError :: SomeOpt -> Maybe String -> String -> OptError
- Options.Harg.Types: [_oeDesc] :: OptError -> String
- Options.Harg.Types: [_oeOpt] :: OptError -> SomeOpt
- Options.Harg.Types: [_oeSource] :: OptError -> Maybe String
- Options.Harg.Types: data OptError
- Options.Harg.Types: toOptError :: Opt a -> Maybe String -> String -> OptError
+ Options.Harg: Rec :: K1 R a x -> Rec p a
+ Options.Harg: [unRec] :: Rec p a -> K1 R a x
+ Options.Harg: newtype Rec p a (x :: k) :: forall k. () => Type -> Type -> k -> Type
+ Options.Harg.Pretty: ppSourceRunErrors :: [SourceRunError] -> String
+ Options.Harg.Sources.DefaultStr: instance (Data.Barbie.Internal.Functor.FunctorB a, Data.Barbie.Internal.Traversable.TraversableB a) => Options.Harg.Sources.Types.RunSource Options.Harg.Sources.DefaultStr.DefaultStrSourceVal a
+ Options.Harg.Sources.Env: instance (Data.Barbie.Internal.Functor.FunctorB a, Data.Barbie.Internal.Traversable.TraversableB a) => Options.Harg.Sources.Types.RunSource Options.Harg.Sources.Env.EnvSourceVal a
+ Options.Harg.Sources.Types: SourceRunError :: Maybe SomeOpt -> String -> String -> SourceRunError
+ Options.Harg.Sources.Types: [_sreError] :: SourceRunError -> String
+ Options.Harg.Sources.Types: [_sreOpt] :: SourceRunError -> Maybe SomeOpt
+ Options.Harg.Sources.Types: [_sreSourceName] :: SourceRunError -> String
+ Options.Harg.Sources.Types: data SourceRunError
+ Options.Harg.Sources.Types: sourceRunError :: forall a. Opt a -> String -> String -> SourceRunError
- Options.Harg.Operations: failParser :: Parser a -> [OptError] -> IO a
+ Options.Harg.Operations: failParser :: Parser a -> [SourceRunError] -> IO a
- Options.Harg.Pretty: ppSource :: Maybe String -> String
+ Options.Harg.Pretty: ppSource :: String -> String
- Options.Harg.Sources: accumSourceResults :: forall a f. TraversableB a => [a (Compose SourceRunResult f)] -> ([OptError], [a (Compose Maybe f)])
+ Options.Harg.Sources: accumSourceResults :: forall a f. TraversableB a => [Either SourceRunError (a (Compose SourceRunResult f))] -> ([SourceRunError], [a (Compose Maybe f)])
- Options.Harg.Sources.DefaultStr: runDefaultStrSource :: forall a f. (FunctorB a, Applicative f) => a (Compose Opt f) -> a (Compose SourceRunResult f)
+ Options.Harg.Sources.DefaultStr: runDefaultStrSource :: forall a f. (FunctorB a, TraversableB a, Applicative f) => a (Compose Opt f) -> Either SourceRunError (a (Compose SourceRunResult f))
- Options.Harg.Sources.Env: runEnvVarSource :: forall a f. (FunctorB a, Applicative f) => Environment -> a (Compose Opt f) -> a (Compose SourceRunResult f)
+ Options.Harg.Sources.Env: runEnvVarSource :: forall a f. (FunctorB a, TraversableB a, Applicative f) => Environment -> a (Compose Opt f) -> Either SourceRunError (a (Compose SourceRunResult f))
- Options.Harg.Sources.JSON: JSONSourceVal :: Value -> JSONSourceVal
+ Options.Harg.Sources.JSON: JSONSourceVal :: ByteString -> JSONSourceVal
- Options.Harg.Sources.JSON: runJSONSource :: forall a f. (FunctorB a, FromJSON (a Maybe), Applicative f) => Value -> a (Compose Opt f) -> a (Compose SourceRunResult f)
+ Options.Harg.Sources.JSON: runJSONSource :: forall a f. (FunctorB a, FromJSON (a Maybe), Applicative f) => ByteString -> a (Compose Opt f) -> Either SourceRunError (a (Compose SourceRunResult f))
- Options.Harg.Sources.Types: runSource :: (RunSource s a, Applicative f) => s -> a (Compose Opt f) -> [a (Compose SourceRunResult f)]
+ Options.Harg.Sources.Types: runSource :: (RunSource s a, Applicative f) => s -> a (Compose Opt f) -> [Either SourceRunError (a (Compose SourceRunResult f))]
- Options.Harg.Sources.YAML: runYAMLSource :: forall a f. (FunctorB a, FromJSON (a Maybe), Applicative f) => ByteString -> a (Compose Opt f) -> a (Compose SourceRunResult f)
+ Options.Harg.Sources.YAML: runYAMLSource :: forall a f. (FunctorB a, FromJSON (a Maybe), Applicative f) => ByteString -> a (Compose Opt f) -> Either SourceRunError (a (Compose SourceRunResult f))
- Options.Harg.Subcommands: explMapSubcommand :: (ExplSubcommands n ts xs acc, All (RunSource s) xs, Applicative f) => SNat n -> s -> AssocListF ts xs (Compose Opt f) -> ([OptError], [Mod CommandFields (VariantF (acc ++ xs) f)])
+ Options.Harg.Subcommands: explMapSubcommand :: (ExplSubcommands n ts xs acc, All (RunSource s) xs, Applicative f) => SNat n -> s -> AssocListF ts xs (Compose Opt f) -> ([SourceRunError], [Mod CommandFields (VariantF (acc ++ xs) f)])
- Options.Harg.Subcommands: mapSubcommand :: (Subcommands ts xs, All (RunSource s) xs, Applicative f) => s -> AssocListF ts xs (Compose Opt f) -> ([OptError], [Mod CommandFields (VariantF xs f)])
+ Options.Harg.Subcommands: mapSubcommand :: (Subcommands ts xs, All (RunSource s) xs, Applicative f) => s -> AssocListF ts xs (Compose Opt f) -> ([SourceRunError], [Mod CommandFields (VariantF xs f)])

Files

CHANGELOG.md view
@@ -1,5 +1,11 @@ # Changelog for harg +## 0.4.1.0 [2019.12.22]++- Parsers now stop immediately if a source error is encountered+- Updated the JSON source to return a bytestring instead of an `aeson` `Value`+- Fix broken deriving for `barbie` typeclasses by re-exporting `Rec`+ ## 0.4.0.0 [2019.09.16]  - Fix wrong name in previous release (`HasDefaultValStr` -> `HasDefaultStr`)@@ -8,10 +14,11 @@  ## 0.3.0.0 [2019.09.16] -- Remove `*With` variants of option constructors and make the `*With` variant behaviour the default-  (meaning now options are constructed using function composition and not `toOpt`)-- Remove `opt` prefix from modifiers. Because `default` is a reserved keyword, this is now named-  `defaultVal` (to mirror `defaultStr`)+- Remove `*With` variants of option constructors and make the `*With` variant+  behaviour the default (meaning now options are constructed using function+  composition and not `toOpt`)+- Remove `opt` prefix from modifiers. Because `default` is a reserved keyword,+  this is now named `defaultVal` (to mirror `defaultStr`)    NOTE: the above introduce breaking changes @@ -19,8 +26,8 @@  - Trigger a parser failure when any option in the sources fails to parse -  NOTE: this introduces a breaking change, in that some parsers that failed silently-        and selected the default (if applicable) will now fail.+  NOTE: this introduces a breaking change, in that some parsers that failed+        silently and selected the default (if applicable) will now fail.  ## 0.1.3.0 [2019.08.28] @@ -28,11 +35,13 @@  ## 0.1.2.0 [2019.08.19] -- Add `optRequired` (renamed to `required` for 0.3.0.0) to mark option as required+- Add `optRequired` (renamed to `required` for 0.3.0.0) to mark option as+  required  ## 0.1.1.0 [2019.08.16] -- Add `optDefaultStr` (renamed to `defaultStr` for 0.3.0.0) to provide defaults as unparsed strings+- Add `optDefaultStr` (renamed to `defaultStr` for 0.3.0.0) to provide defaults+  as unparsed strings - Bump dependencies (`barbies` and `higgledy`)  ## 0.1.0.1 [2019.07.19]
README.lhs view
@@ -1,25 +1,27 @@-# harg+# `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.+`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.+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:+Here are some different usage scenarios. Let's first enable some language+extensions and add some imports:  ``` haskell {-# LANGUAGE DataKinds          #-}@@ -48,8 +50,8 @@  ## One flat (non-nested) datatype -The easiest scenario is when the target configuration type is one single record with no levels of-nesting:+The easiest scenario is when the target configuration type is one single record+with no levels of nesting:  ``` haskell data FlatConfig@@ -64,8 +66,8 @@  (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.+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@@ -101,21 +103,24 @@       ) ``` -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`.+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`+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@@ -130,14 +135,16 @@   = 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`.+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`:+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)@@ -148,16 +155,16 @@       ) ``` -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).+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:+`barbie` types are types of kind `(Type -> Type) -> Type`. The `barbie` type for+`FlatConfig` looks like this:  ``` haskell data FlatConfigB f@@ -170,12 +177,13 @@   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`).+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`:+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@@ -183,8 +191,8 @@   = 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`.+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: @@ -192,48 +200,62 @@ getFlatConfig1 :: IO () getFlatConfig1 = do   FlatConfigB host port dir log <- execOptDef flatConfigOpt1-  print $ runIdentity (FlatConfig <$> host <*> port <*> dir <*> log)+  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`.+`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.+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:+`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 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)+  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)`.+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:+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@@ -242,10 +264,12 @@   :* 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`.+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`.+However, the real value when having flat datatypes comes from the ability to use+`higgledy`.  ### 3. Using `HKD` from `higgledy` @@ -260,14 +284,16 @@   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.+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`).+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 @@ -343,8 +369,9 @@   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`:+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@@ -365,18 +392,24 @@ ``` haskell getConfig1 :: IO () getConfig1 = do-  ConfigB (DbConfigB dbHost dbPort) (ServiceConfigB port log) dir <- execOptDef configOpt1+  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)+  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:+`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@@ -406,14 +439,19 @@ getConfig2 :: IO () getConfig2 = do   ConfigH db service dir <- execOptDef configOpt2-  print $ runIdentity (Config <$> construct db <*> construct service <*> dir)+  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:+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@@ -428,11 +466,16 @@ getConfig3 :: IO () getConfig3 = do   db :* service :* dir <- execOptDef configOpt3-  print $ runIdentity (Config <$> construct db <*> construct service <*> getSingle dir)+  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:+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@@ -460,7 +503,11 @@ getConfig4 :: IO () getConfig4 = do   db :* service :* dir <- execOptDef configOpt4-  print $ runIdentity (Config <$> getNested db <*> getNested service <*> getSingle dir)+  print $ runIdentity $+    Config+    <$> getNested db+    <*> getNested service+    <*> getSingle dir ```  Pretty cool.@@ -470,12 +517,13 @@ `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:+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@@ -483,10 +531,12 @@   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).+`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]@@ -509,10 +559,11 @@  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`:+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@@ -562,8 +613,8 @@   = configOpt4 :+ testConfigOpt :+ ANil ``` -The `ANil` here marks the end of the association list (which is a heterogeneous list that associates-symbols with types).+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: @@ -573,11 +624,16 @@   result <- execCommandsDef subcommandOpt   case result of     HereF (db :* service :* dir)-      -> print $ runIdentity-       $ Config <$> getNested db <*> getNested service <*> getSingle dir+      -> print $ runIdentity $+           Config+           <$> getNested db+           <*> getNested service+           <*> getSingle dir     ThereF (HereF (foo :* bar))-      -> print $ runIdentity-       $ TestConfig <$> getSingle foo <*> getSingle bar+      -> print $ runIdentity $+           TestConfig+           <$> getSingle foo+           <*> getSingle bar ```  Or use `fromVariantF`, which is similar to the `either` function:@@ -588,12 +644,17 @@   result <- execCommandsDef subcommandOpt   fromVariantF result     (\(db :* service :* dir)-       -> print $ runIdentity-       $ Config <$> getNested db <*> getNested service <*> getSingle dir+       -> print $ runIdentity $+            Config+            <$> getNested db+            <*> getNested service+            <*> getSingle dir     )     (\(foo :* bar)-       -> print $ runIdentity-       $ TestConfig <$> getSingle foo <*> getSingle bar+       -> print $ runIdentity $+            TestConfig+            <$> getSingle foo+            <*> getSingle bar     ) ``` @@ -609,17 +670,18 @@   -> r ``` -The signature will accept the appropriate number of functions depending on the length of the type-level list.+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).+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. @@ -674,25 +736,25 @@   = 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:+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:+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:+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@@ -708,8 +770,8 @@           ) ``` -Here, the type of the option for the JSON source is `ConfigFile`. This type is a wrapper around-`FilePath`, which looks like this:+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@@ -717,8 +779,8 @@   | NoConfigFile ``` -This has the advantage that, if the user wants to specify an optional configuration file, they can-simply say:+This has the advantage that, if the user wants to specify an optional+configuration file, they can simply say:  ``` haskell jsonOpt :: Opt ConfigFile@@ -732,17 +794,16 @@ 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.+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-- ~~Be able to provide and get back the same type for multiple subcommands~~-- ~~Integrate config files (e.g. JSON using aeson)~~  # Credits 
README.md view
@@ -1,25 +1,27 @@-# harg+# `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.+`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.+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:+Here are some different usage scenarios. Let's first enable some language+extensions and add some imports:  ``` haskell {-# LANGUAGE DataKinds          #-}@@ -48,8 +50,8 @@  ## One flat (non-nested) datatype -The easiest scenario is when the target configuration type is one single record with no levels of-nesting:+The easiest scenario is when the target configuration type is one single record+with no levels of nesting:  ``` haskell data FlatConfig@@ -64,8 +66,8 @@  (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.+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@@ -101,21 +103,24 @@       ) ``` -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`.+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`+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@@ -130,14 +135,16 @@   = 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`.+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`:+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)@@ -148,16 +155,16 @@       ) ``` -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).+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:+`barbie` types are types of kind `(Type -> Type) -> Type`. The `barbie` type for+`FlatConfig` looks like this:  ``` haskell data FlatConfigB f@@ -170,12 +177,13 @@   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`).+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`:+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@@ -183,8 +191,8 @@   = 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`.+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: @@ -192,48 +200,62 @@ getFlatConfig1 :: IO () getFlatConfig1 = do   FlatConfigB host port dir log <- execOptDef flatConfigOpt1-  print $ runIdentity (FlatConfig <$> host <*> port <*> dir <*> log)+  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`.+`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.+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:+`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 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)+  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)`.+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:+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@@ -242,10 +264,12 @@   :* 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`.+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`.+However, the real value when having flat datatypes comes from the ability to use+`higgledy`.  ### 3. Using `HKD` from `higgledy` @@ -260,14 +284,16 @@   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.+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`).+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 @@ -343,8 +369,9 @@   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`:+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@@ -365,18 +392,24 @@ ``` haskell getConfig1 :: IO () getConfig1 = do-  ConfigB (DbConfigB dbHost dbPort) (ServiceConfigB port log) dir <- execOptDef configOpt1+  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)+  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:+`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@@ -406,14 +439,19 @@ getConfig2 :: IO () getConfig2 = do   ConfigH db service dir <- execOptDef configOpt2-  print $ runIdentity (Config <$> construct db <*> construct service <*> dir)+  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:+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@@ -428,11 +466,16 @@ getConfig3 :: IO () getConfig3 = do   db :* service :* dir <- execOptDef configOpt3-  print $ runIdentity (Config <$> construct db <*> construct service <*> getSingle dir)+  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:+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@@ -460,7 +503,11 @@ getConfig4 :: IO () getConfig4 = do   db :* service :* dir <- execOptDef configOpt4-  print $ runIdentity (Config <$> getNested db <*> getNested service <*> getSingle dir)+  print $ runIdentity $+    Config+    <$> getNested db+    <*> getNested service+    <*> getSingle dir ```  Pretty cool.@@ -470,12 +517,13 @@ `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:+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@@ -483,10 +531,12 @@   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).+`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]@@ -509,10 +559,11 @@  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`:+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@@ -562,8 +613,8 @@   = configOpt4 :+ testConfigOpt :+ ANil ``` -The `ANil` here marks the end of the association list (which is a heterogeneous list that associates-symbols with types).+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: @@ -573,11 +624,16 @@   result <- execCommandsDef subcommandOpt   case result of     HereF (db :* service :* dir)-      -> print $ runIdentity-       $ Config <$> getNested db <*> getNested service <*> getSingle dir+      -> print $ runIdentity $+           Config+           <$> getNested db+           <*> getNested service+           <*> getSingle dir     ThereF (HereF (foo :* bar))-      -> print $ runIdentity-       $ TestConfig <$> getSingle foo <*> getSingle bar+      -> print $ runIdentity $+           TestConfig+           <$> getSingle foo+           <*> getSingle bar ```  Or use `fromVariantF`, which is similar to the `either` function:@@ -588,12 +644,17 @@   result <- execCommandsDef subcommandOpt   fromVariantF result     (\(db :* service :* dir)-       -> print $ runIdentity-       $ Config <$> getNested db <*> getNested service <*> getSingle dir+       -> print $ runIdentity $+            Config+            <$> getNested db+            <*> getNested service+            <*> getSingle dir     )     (\(foo :* bar)-       -> print $ runIdentity-       $ TestConfig <$> getSingle foo <*> getSingle bar+       -> print $ runIdentity $+            TestConfig+            <$> getSingle foo+            <*> getSingle bar     ) ``` @@ -609,17 +670,18 @@   -> r ``` -The signature will accept the appropriate number of functions depending on the length of the type-level list.+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).+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. @@ -674,25 +736,25 @@   = 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:+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:+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:+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@@ -708,8 +770,8 @@           ) ``` -Here, the type of the option for the JSON source is `ConfigFile`. This type is a wrapper around-`FilePath`, which looks like this:+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@@ -717,8 +779,8 @@   | NoConfigFile ``` -This has the advantage that, if the user wants to specify an optional configuration file, they can-simply say:+This has the advantage that, if the user wants to specify an optional+configuration file, they can simply say:  ``` haskell jsonOpt :: Opt ConfigFile@@ -732,17 +794,16 @@ 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.+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-- ~~Be able to provide and get back the same type for multiple subcommands~~-- ~~Integrate config files (e.g. JSON using aeson)~~  # Credits 
harg.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.2  name:           harg-version:        0.4.0.0+version:        0.4.1.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
src/Options/Harg.hs view
@@ -81,6 +81,7 @@   , B.FunctorB   , B.TraversableB   , B.ProductB+  , B.Rec (..)    -- *** higgledy   , HKD.HKD
src/Options/Harg/Cmdline.hs view
@@ -78,9 +78,7 @@           Optparse.flag def active modifiers   where     mDef-      = case getCompose sources of-          Nothing -> _optDefaultVal-          Just x  -> Just x+      = getCompose sources <|> _optDefaultVal     modifiers       = foldMap (fromMaybe mempty)           [ Optparse.long <$> _optLong
src/Options/Harg/Construct.hs view
@@ -344,8 +344,7 @@ -- with the default 'Left' value @unable to parse: \<input\>@. parseWith   :: (String -> Maybe a)  -- ^ Original parser-  -> String  -- ^ Input-  -> Either String a+  -> (String -> Either String a) parseWith parser s   = maybe (Left err) Right (parser s)   where
src/Options/Harg/Operations.hs view
@@ -17,14 +17,14 @@ import           Options.Harg.Het.HList          (AssocListF, MapAssocList(..)) import           Options.Harg.Het.Prod           ((:*)(..)) import           Options.Harg.Het.Variant        (VariantF)-import           Options.Harg.Pretty             (ppOptErrors)+import           Options.Harg.Pretty             (ppSourceRunErrors) import           Options.Harg.Sources            ( accumSourceResults                                                  , DefaultSources, defaultSources                                                  , HiddenSources, hiddenSources                                                  )-import           Options.Harg.Sources.Types      (GetSource(..), RunSource(..))+import           Options.Harg.Sources.Types      (GetSource(..), RunSource(..), SourceRunError) import           Options.Harg.Subcommands        (Subcommands(..))-import           Options.Harg.Types              (HargCtx(..), getCtx, Opt, OptError)+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,@@ -209,7 +209,7 @@  failParser   :: Optparse.Parser a-  -> [OptError]+  -> [SourceRunError]   -> IO a failParser parser errs   = Optparse.handleParseResult (Optparse.Failure failure)@@ -223,7 +223,7 @@     parserInfo       = Optparse.info (Optparse.helper <*> parser) Optparse.forwardOptions     errStr-      = ppOptErrors errs+      = ppSourceRunErrors errs  -- | Run the optparse-applicative parser and return the -- 'Optparse.ParserResult'
src/Options/Harg/Pretty.hs view
@@ -1,8 +1,10 @@ module Options.Harg.Pretty where -import Data.List          (intercalate, nubBy)-import Data.Maybe         (fromMaybe)+import Control.Applicative        ((<|>))+import Data.List                  (intercalate)+import Data.Maybe                 (fromMaybe) +import Options.Harg.Sources.Types import Options.Harg.Types  @@ -12,31 +14,38 @@ ppHelp Opt{..}   = (<> ppEnvVar _optEnvVar) <$> _optHelp -ppOptErrors-  :: [OptError]+ppSourceRunErrors+  :: [SourceRunError]   -> String-ppOptErrors+ppSourceRunErrors   = intercalate "\n\n"-  . map ppOptError-  . nubBy cmpOptErr+  . map ppSourceRunError   where-    cmpOptErr (OptError (SomeOpt l) sl dl) (OptError (SomeOpt r) sr dr)-      =  _optLong l == _optLong r && sl == sr && dl == dr-    ppOptError :: OptError -> String-    ppOptError (OptError (SomeOpt opt) src desc)+    ppSourceRunError :: SourceRunError -> String+    ppSourceRunError (SourceRunError Nothing src desc)+      =  "error: "+      <> desc+      <> "\n\t"+      <> ppSource src++    ppSourceRunError (SourceRunError (Just (SomeOpt opt)) src desc)       =  "option "-      <> fromMaybe "<no opt name>" (_optLong opt)+      <> optId opt       <> ": "       <> desc       <> "\n\t"       <> ppSource src       <> ppEnvVar (_optEnvVar opt) +    optId Opt{..}+      = fromMaybe "<no name available>"+        $ _optLong <|> (pure <$> _optShort) <|> _optMetavar+ ppSource-  :: Maybe String+  :: String   -> String-ppSource-  = maybe "" $ \s -> " (source: " <> s <> ")"+ppSource s+  = " (source: " <> s <> ")"  ppEnvVar   :: Maybe String
src/Options/Harg/Sources.hs view
@@ -1,7 +1,3 @@-{-# LANGUAGE AllowAmbiguousTypes  #-}-{-# LANGUAGE PolyKinds            #-}-{-# LANGUAGE TypeFamilies         #-}-{-# LANGUAGE UndecidableInstances #-} module Options.Harg.Sources where  import           Data.Foldable              (foldr')@@ -12,7 +8,6 @@ import           Options.Harg.Sources.DefaultStr import           Options.Harg.Sources.Env import           Options.Harg.Sources.Types-import           Options.Harg.Types   -- | Accumulate all the successful source results and return them,@@ -20,26 +15,30 @@ accumSourceResults   :: forall a f.      B.TraversableB a-  => [a (Compose SourceRunResult f)]-  -> ([OptError], [a (Compose Maybe f)])+  => [Either SourceRunError (a (Compose SourceRunResult f))]+  -> ([SourceRunError], [a (Compose Maybe f)]) accumSourceResults   = foldr' accumResult ([], [])   where     accumResult-      :: a (Compose SourceRunResult f)-      -> ([OptError], [a (Compose Maybe f)])-      -> ([OptError], [a (Compose Maybe f)])+      :: Either SourceRunError (a (Compose SourceRunResult f))+      -> ([SourceRunError], [a (Compose Maybe f)])+      -> ([SourceRunError], [a (Compose Maybe f)])     accumResult res (e, a)-      = case B.btraverse go res of-          (e', a') -> (e' <> e, a' : a)+      = case res of+          Left sre -> (sre : e, a)+          Right res' ->+            case B.btraverse go res' of+              (e', a') -> (e' <> e, a' : a)     go       :: Compose SourceRunResult f x-      -> ([OptError], Compose Maybe f x)+      -> ([SourceRunError], Compose Maybe f x)     go x       = case getCompose x of-          OptFoundNoParse e -> ([e], Compose Nothing)-          OptParsed a       -> ([], Compose (Just a))-          _                 -> ([], Compose Nothing)+          OptParsed a+            -> ([], Compose (Just a))+          OptNotFound+            -> ([], Compose Nothing)  type HiddenSources = DefaultStrSource 
src/Options/Harg/Sources/DefaultStr.hs view
@@ -28,30 +28,35 @@     = pure DefaultStrSourceVal  instance-    B.FunctorB a => RunSource DefaultStrSourceVal a where+    ( B.FunctorB a+    , B.TraversableB a+    ) => RunSource DefaultStrSourceVal a where   runSource DefaultStrSourceVal opt     = [runDefaultStrSource opt] +-- TODO: this looks very similar to EnvSource, perhaps unify runDefaultStrSource   :: forall a f.      ( B.FunctorB a+     , B.TraversableB a      , Applicative f      )   => a (Compose Opt f)-  -> a (Compose SourceRunResult f)+  -> Either SourceRunError (a (Compose SourceRunResult f)) runDefaultStrSource-  = B.bmap go+  = B.btraverse go   where-    go :: Compose Opt f x -> Compose SourceRunResult f x+    go+      :: Compose Opt f x+      -> Either SourceRunError (Compose SourceRunResult f x)     go (Compose opt@Opt{..})-      = case _optDefaultStr of-          Nothing-            -> Compose $ pure <$> OptNotFound-          Just str-            -> Compose $ tryParse str+      = maybe toNotFound parse _optDefaultStr       where-        tryParse-          = either-              (OptFoundNoParse . toOptError opt (Just "DefaultStrSource"))-              OptParsed-          . _optReader+        parse+          = either toErr toParsed . _optReader+        toNotFound+          = Right $ Compose $ pure <$> OptNotFound+        toErr+          = Left . sourceRunError opt "DefaultStrSource"+        toParsed+          = Right . Compose . OptParsed
src/Options/Harg/Sources/Env.hs view
@@ -28,7 +28,9 @@     = pure (EnvSourceVal _hcEnv)  instance-    B.FunctorB a => RunSource EnvSourceVal a where+    ( B.FunctorB a+    , B.TraversableB a+    ) => RunSource EnvSourceVal a where   runSource (EnvSourceVal e) opt     = [runEnvVarSource e opt] @@ -43,24 +45,26 @@ runEnvVarSource   :: forall a f.      ( B.FunctorB a+     , B.TraversableB a      , Applicative f      )   => Environment   -> a (Compose Opt f)-  -> a (Compose SourceRunResult f)+  -> Either SourceRunError (a (Compose SourceRunResult f)) runEnvVarSource env-  = B.bmap go+  = B.btraverse go   where-    go :: Compose Opt f x -> Compose SourceRunResult f x+    go+      :: Compose Opt f x+      -> Either SourceRunError (Compose SourceRunResult f x)     go (Compose opt@Opt{..})-      = case _optEnvVar of-          Nothing-            -> Compose $ pure <$> OptNotFound-          Just envVar-            -> Compose $ maybe OptNotFound tryParse (lookupEnv env envVar)+      = maybe toNotFound (parse . lookupEnv env) _optEnvVar       where-        tryParse-          = either-              (OptFoundNoParse . toOptError opt (Just "EnvSource"))-              OptParsed-          . _optReader+        parse+          = maybe toNotFound (either toErr toParsed . _optReader)+        toNotFound+          = Right $ Compose $ pure <$> OptNotFound+        toErr+          = Left . sourceRunError opt "EnvSource"+        toParsed+          = Right . Compose . OptParsed
src/Options/Harg/Sources/JSON.hs view
@@ -7,6 +7,7 @@ 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@@ -23,19 +24,13 @@ -- the user has specified @defaultVal NoConfigFile@. It holds the contents of -- the JSON file as a 'JSON.Value'. data JSONSourceVal-  = JSONSourceVal JSON.Value+  = JSONSourceVal LBS.ByteString   | JSONSourceNotRequired  instance GetSource JSONSource Identity where   type SourceVal JSONSource = JSONSourceVal-  getSource _ctx (JSONSource (Identity (ConfigFile path))) = do-    contents <- readFileLBS path-    case JSON.eitherDecode contents of-      Right json-        -> pure $ JSONSourceVal json-      Left err-        -> printErrAndExit-            $ "Error decoding " <> path <> " to JSON: " <> err+  getSource _ctx (JSONSource (Identity (ConfigFile path)))+    = JSONSourceVal <$> readFileLBS path   getSource _ctx (JSONSource (Identity NoConfigFile))     = pure JSONSourceNotRequired @@ -54,20 +49,22 @@      , JSON.FromJSON (a Maybe)      , Applicative f      )-  => JSON.Value+  => LBS.ByteString   -> a (Compose Opt f)-  -> a (Compose SourceRunResult f)-runJSONSource json opt+  -> Either SourceRunError (a (Compose SourceRunResult f))+runJSONSource json _opt   = case res of-      JSON.Success v -> B.bmap toSuccess v-      JSON.Error _e  -> B.bmap toFailure opt+      Right v  -> Right $ B.bmap toSuccess v+      Left exc -> Left $ toError exc   where-    res :: JSON.Result (a Maybe)+    res :: Either String (a Maybe)     res-      = JSON.fromJSON json+      = JSON.eitherDecode json+     toSuccess :: Maybe x -> Compose SourceRunResult f x     toSuccess mx       = Compose $ pure <$> maybe OptNotFound OptParsed mx-    toFailure :: Compose Opt f x -> Compose SourceRunResult f x-    toFailure _-      = Compose $ pure <$> OptNotFound++    toError :: String -> SourceRunError+    toError+      = SourceRunError Nothing "JSONSource"
src/Options/Harg/Sources/Types.hs view
@@ -5,7 +5,7 @@  import Data.Functor.Compose  (Compose (..)) import Data.Kind             (Type)-import           Data.String (IsString(..))+import Data.String           (IsString(..))  import Options.Harg.Het.Prod import Options.Harg.Types@@ -14,13 +14,28 @@ -- | Holds errors that occur when running a source. data SourceRunResult a   = OptNotFound  -- ^ Source doesn't include the option-  | OptFoundNoParse OptError  -- ^ Option cannot be parsed from source   | OptParsed a  -- ^ Successful parsing   deriving Functor --- | This class enables a type that describes a source to fetch--- the source contents, potentially producing side effects (e.g. reading--- a file).+data SourceRunError+  = SourceRunError+      { _sreOpt        :: Maybe SomeOpt+      , _sreSourceName :: String+      , _sreError      :: String+      }++-- | Create a 'SourceRunError' by existentially wrapping an option in 'SomeOpt'.+sourceRunError+  :: forall a.+     Opt a+  -> String+  -> String+  -> SourceRunError+sourceRunError+  = SourceRunError . Just . SomeOpt++-- | This class enables a type that describes a source to fetch the source+-- contents, potentially producing side effects (e.g. reading a file). class GetSource     (c :: (Type -> Type) -> Type)     (f :: (Type -> Type)) where@@ -45,7 +60,7 @@     :: Applicative f     => s     -> a (Compose Opt f)-    -> [a (Compose SourceRunResult f)]+    -> [Either SourceRunError (a (Compose SourceRunResult f))]  instance     ( RunSource l a
src/Options/Harg/Sources/YAML.hs view
@@ -53,22 +53,20 @@      )   => BS.ByteString   -> a (Compose Opt f)-  -> a (Compose SourceRunResult f)-runYAMLSource yaml opt+  -> Either SourceRunError (a (Compose SourceRunResult f))+runYAMLSource yaml _opt   = case res of-      Right v  -> B.bmap toSuccess v-      Left exc -> B.bmap (toFailure exc) opt+      Right v  -> Right $ B.bmap toSuccess v+      Left exc -> Left $ toError exc   where     res :: Either YAML.ParseException (a Maybe)     res       = YAML.decodeEither' yaml+     toSuccess :: Maybe x -> Compose SourceRunResult f x     toSuccess mx       = Compose $ pure <$> maybe OptNotFound OptParsed mx-    toFailure-      :: YAML.ParseException-      -> Compose Opt f x-      -> Compose SourceRunResult f x-    toFailure exc (Compose o)-      = Compose-        $ OptFoundNoParse (toOptError o (Just "YAMLSource") (displayException exc))++    toError :: YAML.ParseException -> SourceRunError+    toError exc+      = SourceRunError Nothing "YAMLSource" (displayException exc)
src/Options/Harg/Subcommands.hs view
@@ -60,7 +60,7 @@        )     => s     -> AssocListF ts xs (Compose Opt f)-    -> ([OptError], [Optparse.Mod Optparse.CommandFields (VariantF xs f)])+    -> ([SourceRunError], [Optparse.Mod Optparse.CommandFields (VariantF xs f)])  instance ExplSubcommands Z ts xs '[] => Subcommands ts xs where   mapSubcommand = explMapSubcommand @Z @ts @xs @'[] SZ@@ -79,7 +79,7 @@     => SNat n     -> s     -> AssocListF ts xs (Compose Opt f)-    -> ([OptError], [Optparse.Mod Optparse.CommandFields (VariantF (acc ++ xs) f)])+    -> ([SourceRunError], [Optparse.Mod Optparse.CommandFields (VariantF (acc ++ xs) f)])  instance ExplSubcommands n '[] '[] acc where   explMapSubcommand _ _ _ = ([], [])
src/Options/Harg/Types.hs view
@@ -72,17 +72,6 @@       , _aReader     :: OptReader a       } --- | Datatype that holds errors that arise when running the sources.--- The reason why this is the only place where errors occur is that,--- if something goes wrong when running the parser, it will be handled--- by @optparse-applicative@.-data OptError-  = OptError-      { _oeOpt    :: SomeOpt       -- ^ Existentially quantified 'Opt'-      , _oeSource :: Maybe String  -- ^ Source name-      , _oeDesc   :: String        -- ^ Error description-      }- -- | Existential wrapper for 'Opt', so that many options can be carried in -- a list. data SomeOpt where@@ -119,11 +108,3 @@ pureCtx :: Environment -> Args -> HargCtx pureCtx   = HargCtx--toOptError-  :: Opt a-  -> Maybe String-  -> String-  -> OptError-toOptError-  = OptError . SomeOpt