[Haskell-cafe] Config Files and monoids

Matt parsonsmatt at gmail.com
Thu May 24 22:03:30 UTC 2018


For a "fully general" approach, the problem is well expressed by the
"higher kinded data" pattern:
http://reasonablypolymorphic.com/blog/higher-kinded-data/

A `Config f = Config { configFoo :: f Foo, ... }` type would use either the
First or Last monoids, depending on if you want earlier updates to take
precedence over later ones. Then, you would get a `Config First` from your
CLI parser, a `Config First` from your environment variable parser, and a
`Config First` from your config file parser. After `mappend`ing them all
together, you'd use a `gtraverse` function with a signature like: `Config
First -> Either [Text] (Config Identity)` -- you'd either have a list of
all fields that were missing, or a complete Config.

Matt Parsons

On Thu, May 24, 2018 at 3:00 PM, Olaf Klinke <olf at aatal-apotheke.de> wrote:

> Dear cafe,
>
> a recent post here [1] mentioned that configurations, such as the ones
> read from a config file, can be given Monoid instances, where mempty is the
> empty or default configuration and mappend merges two partial
> configurations, producing a more complete one. The vgrep package explicitly
> does this, for instance. Although the ConfigParser type from the ConfigFile
> package has a binary 'merge' operation, it does define neither a Monoid not
> a Semigroup instance.
>
> I'm struggling to make the concept of monoidal configuration work when
> there is no sensible default configuration. Suppose my configuration type
> is
>
> data Config = Config {foo :: Bool, bar :: Int}
>
> with no reasonable default, e.g.
>
> emptyConfig = Config {
>   foo = error "you did not specify option foo",
>   bar = error "you did not specify option bar"
>   }
>
> Some configuration monoids seem to have the second operand override the
> first, or the other way around. However, I wish that when
> cfg1 = emptyConfig {foo = True}
> cfg2 = emptyConfig {bar = 4}
> then cfg1 <> cfg2 == Config {foo = True, bar = 4}.
>
> So it seems that for mappend to work as intended one needs a terminating
> function that tells me if a record field is already defined, e.g. when all
> fields are Maybes. Vgrep.Environment.Config.Monoid does it this way. My
> solution so far was to resort to the monoid of endofunctions (as the
> getflag package does), that is, define
>
> cfg1, cfg2 :: Config -> Config
> cfg1 = \cfg -> cfg {foo = True}
> cfg2 = \cfg -> cfg {bar = 4}
>
> And then build (cfg1.cfg2) emptyConfig. (Alternatively, one might
> structure these as lenses instead of endofunctions, see e.g.
> Data.Monoid.Endo.Fold in the endo package.)
> Thus I arrived at
>
> class Config cfg where
>   emptyConfig   :: cfg -- may contain some defaults
>   configOptions :: [Parser (cfg -> cfg)]
>
> Do you think every other concept of configuration parsing can be cast into
> this typeclass?
> -- Olaf
>
> [1] https://mail.haskell.org/pipermail/haskell-cafe/2018-May/129063.html
> _______________________________________________
> Haskell-Cafe mailing list
> To (un)subscribe, modify options or view archives go to:
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
> Only members subscribed via the mailman list are allowed to post.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20180524/ad64b605/attachment.html>


More information about the Haskell-Cafe mailing list