Krzysztof Skrzętnicki | 10 May 07:45 2013

Reinventing a solution to configuration problem

Hello Cafe,

I came up with (actually reinvented [1]) a solution to configuration problem. For example configuration:

data Config = Config { port :: Int, verbosity :: Int, logfile :: FilePath } deriving Show

the basic idea is to use implicit parameters:

runServer :: (?config :: Config) => IO ()

The problem is that it is tiresome to decorate all functions in this way. It is also non composable. For example if we switch to using two separate configuration entities like this:

data LogConfig = LogConfig { verbosity :: Int, logfile :: FilePath }
data NetConfig = NetConfig { port :: Int }

we suddenly have to fix types all over the place. Yuck.

The extended idea here (compared to [2]) is to use type synonym with all required parameters. This is simple (but requires at least Rank2Types):

type ConfIO a = (?config :: Config) => IO a

All the functions requiring config will now be simply in ConfIO monad which is really IO with a bonus. A great feature of this solution is that we don't need to use liftIO anywhere.

The core problem with this approach is that at least current version of GHC fails to propagate implicit parameters the way it propagate typeclass requirements. For example I would like the following function to have (inferred) type 

-- printConfig :: (?config :: Config) => IO ()
printConfig = print (?config :: Config)

Instead GHC will complain about unbound implicit parameter. Similar problem from GHCi session:

Prelude> let f = print (?conf :: Int)
    Unbound implicit parameter (?conf::Int)
      arising from a use of implicit parameter `?conf'
    In the first argument of `print', namely `(?conf :: Int)'
    In the expression: print (?conf :: Int)
    In an equation for `f': f = print (?conf :: Int)

For reason I don't fully understand this is fixed by disabling monomorphic restriction. The documentation mentions the problem but in a different context:

TL;DR: A nice solution to configuration problem is to use implicit parameters and type synonym for IO monad extended with implicit configuration parameters. It requires Rank2Types for defining the synonym and NoMonomorphismRestriction for lightweight use.

Best regards,
Krzysztof Skrzętnicki

[1] I only realised this after starting the writing of this email. The implicit parameters solution is given at least in [2]. Somehow I didn't came across it before.

Haskell-Cafe mailing list
Haskell-Cafe <at>
oleg | 11 May 07:06 2013

Re: Reinventing a solution to configuration problem

I guess you might like then
which discusses implicit parameters and their drawbacks (see Sec 6.2).