<div dir="ltr">For a "fully general" approach, the problem is well expressed by the "higher kinded data" pattern: <a href="http://reasonablypolymorphic.com/blog/higher-kinded-data/">http://reasonablypolymorphic.com/blog/higher-kinded-data/</a><br><br>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.<br></div><div class="gmail_extra"><br clear="all"><div><div class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div>Matt Parsons</div></div></div></div>
<br><div class="gmail_quote">On Thu, May 24, 2018 at 3:00 PM, Olaf Klinke <span dir="ltr"><<a href="mailto:olf@aatal-apotheke.de" target="_blank">olf@aatal-apotheke.de</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Dear cafe, <br>
<br>
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. <br>
<br>
I'm struggling to make the concept of monoidal configuration work when there is no sensible default configuration. Suppose my configuration type is <br>
<br>
data Config = Config {foo :: Bool, bar :: Int}<br>
<br>
with no reasonable default, e.g.<br>
<br>
emptyConfig = Config {<br>
foo = error "you did not specify option foo",<br>
bar = error "you did not specify option bar"<br>
}<br>
<br>
Some configuration monoids seem to have the second operand override the first, or the other way around. However, I wish that when<br>
cfg1 = emptyConfig {foo = True}<br>
cfg2 = emptyConfig {bar = 4}<br>
then cfg1 <> cfg2 == Config {foo = True, bar = 4}.<br>
<br>
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.<wbr>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<br>
<br>
cfg1, cfg2 :: Config -> Config<br>
cfg1 = \cfg -> cfg {foo = True}<br>
cfg2 = \cfg -> cfg {bar = 4}<br>
<br>
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.) <br>
Thus I arrived at<br>
<br>
class Config cfg where<br>
emptyConfig :: cfg -- may contain some defaults<br>
configOptions :: [Parser (cfg -> cfg)]<br>
<br>
Do you think every other concept of configuration parsing can be cast into this typeclass? <br>
-- Olaf<br>
<br>
[1] <a href="https://mail.haskell.org/pipermail/haskell-cafe/2018-May/129063.html" rel="noreferrer" target="_blank">https://mail.haskell.org/<wbr>pipermail/haskell-cafe/2018-<wbr>May/129063.html</a><br>
______________________________<wbr>_________________<br>
Haskell-Cafe mailing list<br>
To (un)subscribe, modify options or view archives go to:<br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-<wbr>bin/mailman/listinfo/haskell-<wbr>cafe</a><br>
Only members subscribed via the mailman list are allowed to post.</blockquote></div><br></div>