When configuring our applications, often the way that field is defined is the same as the way the field is used:
data CfgMyHostName = CfgMyHostName Text
Other times, they differ. Let's make this formal in a typeclass:
data UsagePhase = ConfigTime | RunTime -- Used for promotion to types
class Config (a :: UsagePhase -> *) where
  type Phase (p :: UsagePhase) a = r | r -> a
  toRunTime :: Phase ConfigTime a -> IO (Phase RunTime a)
data DatabaseConfig (p :: UsagePhase)
instance Config DatabaseConfig where
  type Phase ConfigTime DatabaseConfig = ConnectInfo
  type Phase RunTime    DatabaseConfig = ConnectionPool
  toRunTime = connect
A typical service config has many fields, with some in each category. Parameterizing the smaller components that we will compose together lets us write the big composite record once, rather than twice (once for the config specification, once for the runtime data). This is similar to the idea in the 'Trees that Grow' paper:
data UiServerConfig (p :: UsagePhase) = CfgUiServerC {
  userDatabase  :: Phase p DatabaseConfig
  cmsDatabase   :: Phase p DatabaseConfig
  ...
  kinesisStream :: Phase p KinesisConfig
  myHostName    :: CfgMyHostName 
  myPort        :: Int
}
UiServerConfig is one of many such services I'd like to configure, so it
would be nice to derive Generic for such record types, and to add a
default toRunTime implementation to the Config class. This is where
we get stuck.
Given a type parameterized like data Foo f = Foo { foo :: TypeFn f Int, bar :: String},
how do I generically derive a traversal for any type like Foo which affects
every TypeFn record field (recursively)?
As just one example of my confusion, I attempted to use generics-sop like this:
gToRunTime :: (Generic a, All2 Config xs)
           => Phase ConfigTime xs
           -> IO (Phase RunTime xs)
gToRunTime = undefined
This fails because xs :: [[*]], but Config takes a type argument with kind a :: ConfigPhase -> * 
Any hints about what to read in order to get untangled would really be appreciated. Full solutions are acceptable too :)
 
                        
Edit: Updated to automatically derive the
AtoBclass.Here's a solution that appears to work.
Generic Phase Mapping without a Monad
Here are the preliminaries:
Now, suppose we have a
Phase:and a
Selectorfor the field:with the idea that there's a type class with both (1) an associated type family giving the concrete field types associated with a selector for each possible phase and (2) an interface for mapping between phases:
Given a record with a generic instance incorporating both
Fields and non-Fieldsand a
Foo 'Avalue:we'd like to define a generic phase mapping
gAtoB:that uses per-field phase maps
fieldAtoBfrom theIsFieldtype class.The key step is defining a separate type class
AtoBdedicated to the phaseA-to-Btransition to act as a bridge to theIsFieldtype class. ThisAtoBtype class will be used in conjuction with thegenerics-sopmachinery to constrain/match the concrete phaseAandBtypes field by field and dispatch to the appropriatefieldAtoBphase mapping function. Here's the class:Fortunately, instances can be automatically derived for
Fields, though it requires the (mostly harmless)UndecidableInstancesextension:and we can define an instance for non-
Fields:Note one limitation here -- if you define a
Fieldwith equal concrete types in different phases, this overlapping instance withfieldAtoB' = idwill be used andfieldAtoBwill be ignored.Now, for a particular selector
Barwhose underlying types should beBarAandBarBin the respective phases, we can define the followingIsFieldinstance:We can provide a similar definition for
Baz:Now, we can define the generic
gAtoBtransformation like so:There might be a way to do this with
generics-sopcombinators instead of this explicit definition, but I couldn't figure it out.Anyway,
gAtoBworks onFoorecords, as per the definition offoo1above, but it also works onQuuxrecords:Note that I've used selectors with a
Selectordata kind, but you could rewrite this to use selectors of type(a :: Phase -> *), as I've done in the example at the end.Generic Phase Traversal over a Monad
Now, you needed this to happen over the
IOmonad. Here's a modified version that does that:Adapted to Your Problem
And here's a version rewritten to hew as closely to your original design as possible. Again a key limitation is that a
Configwith equal configuration-time and run-time types will usetoRunTime' = returnand not any other definition given in itsConfiginstance.