<div dir="ltr">To get the static record of names you need to make good error messages for the missing things, I think it should also be possible to write a GHC.Generics function with an end result like:<div><br></div><div>gNames :: (GNames r) => r (Const String)</div><div><br></div><div>by digging through the metadata for each field in the record type r, extracting the name, using Const . fromString, and packaging it up in the record?</div><div>But exactly how the Generic1 machinery for this works is escaping me at this hour. Perhaps someone can help fill in the details?</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, May 24, 2018 at 6:45 PM, lennart spitzner <span dir="ltr"><<a href="mailto:hexagoxel@hexagoxel.de" target="_blank">hexagoxel@hexagoxel.de</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Thanks matt for this pointer. I have also used the higher-kinded approach in the past, but using a slightly different abstraction. This is introduced in a very recent post [1]. I think this gives you the custom `gtraverse` you describe essentially for free (I assume you'd still need to write that, given that it has a custom type, right?). However it does not use the type family trick to avoid the `Identity` wrappers.<br>
<br>
The examples in my post still mostly assume that there is a default config, but I think you could work around this. This would involve the `CZipWithM` class instead of just `CZipWith`: The simple path would be<br>
<br>
cTraverse (fmap Identity) :: MyConfig Option -> Option (MyConfig Identity)<br>
<br>
which roughly corresponds to `gvalidate` from the "Higher-Kinded Data" post. The downside is that a Nothing result would not tell you which field(s) were missing. To fix that, you could define a static value of type `MyConfig (Const String)` that adds a value-level name to each field, and use `cZipWithM` to produce a `Either String (MyConfig Identity)` or perhaps even `Either [String] (MyConfig Identity)` by using the right traversal monad.<br>
<br>
Hope this helps.<br>
<br>
-- lennart<br>
<br>
[1] <a href="http://hexagoxel.de/postsforpublish/posts/2018-05-24-program-configuration.html" rel="noreferrer" target="_blank">http://hexagoxel.de/<wbr>postsforpublish/posts/2018-05-<wbr>24-program-configuration.html</a><br>
<div class="gmail-HOEnZb"><div class="gmail-h5"><br>
<br>
On 25/05/18 00:03, Matt wrote:<br>
> For a "fully general" approach, the problem is well expressed by the<br>
> "higher kinded data" pattern:<br>
> <a href="http://reasonablypolymorphic.com/blog/higher-kinded-data/" rel="noreferrer" target="_blank">http://reasonablypolymorphic.<wbr>com/blog/higher-kinded-data/</a><br>
> <br>
> A `Config f = Config { configFoo :: f Foo, ... }` type would use either the<br>
> First or Last monoids, depending on if you want earlier updates to take<br>
> precedence over later ones. Then, you would get a `Config First` from your<br>
> CLI parser, a `Config First` from your environment variable parser, and a<br>
> `Config First` from your config file parser. After `mappend`ing them all<br>
> together, you'd use a `gtraverse` function with a signature like: `Config<br>
> First -> Either [Text] (Config Identity)` -- you'd either have a list of<br>
> all fields that were missing, or a complete Config.<br>
> <br>
> Matt Parsons<br>
> <br>
> On Thu, May 24, 2018 at 3:00 PM, Olaf Klinke <<a href="mailto:olf@aatal-apotheke.de">olf@aatal-apotheke.de</a>> wrote:<br>
> <br>
>> Dear cafe,<br>
>><br>
>> a recent post here [1] mentioned that configurations, such as the ones<br>
>> read from a config file, can be given Monoid instances, where mempty is the<br>
>> empty or default configuration and mappend merges two partial<br>
>> configurations, producing a more complete one. The vgrep package explicitly<br>
>> does this, for instance. Although the ConfigParser type from the ConfigFile<br>
>> package has a binary 'merge' operation, it does define neither a Monoid not<br>
>> a Semigroup instance.<br>
>><br>
>> I'm struggling to make the concept of monoidal configuration work when<br>
>> there is no sensible default configuration. Suppose my configuration type<br>
>> 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<br>
>> 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<br>
>> function that tells me if a record field is already defined, e.g. when all<br>
>> fields are Maybes. Vgrep.Environment.Config.<wbr>Monoid does it this way. My<br>
>> solution so far was to resort to the monoid of endofunctions (as the<br>
>> 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<br>
>> structure these as lenses instead of endofunctions, see e.g.<br>
>> 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<br>
>> 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.<br>
> <br>
> <br>
> <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.<br>
> <br>
<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.</div></div></blockquote></div><br><br clear="all"><div><br></div>-- <br><div class="gmail_signature" data-smartmail="gmail_signature">J. Douglas McClean<br><br>(781) 561-5540 (cell)</div>
</div>