On configuration files (part 2)


I made my point against custom configuration languages with programming features in part 1. This time I will talk about using full-blown programming languages to describe program configuration. There are two reasons to do so -- the simplicity of implementation and configuration flexibility.

Using the language of implementation as the configuration language simplifies things enabling the use of native "source/eval/import" features but writes the choice of implementation language into the stone. Remember, if it is possible to write something complex with configuration language, somebody will do so; should you change implementation language (or merely switch from Python2 to Python3), keeping compatibility with old configurations will become a nightmare. I strongly advise against this approach.

It is possible to use a full-blown programming language to attain configuration flexibility. For example, if a program needs some password as a configuration, but a user is not comfortable with storing the password and prefers to be asked interactively for that password every time, he can achieve it using input/output functions. But there is a better way to do so.

The program can allow the configuration file to be executable, in which case the program executes it and uses its output instead. The following snippet in Python implements this idea:

config_file = "./config.json"
if os.access(config_file, os.X_OK):
    config = json.loads(subprocess.check_output(config_file))
else:
    with open(config_file) as fp:
        config = json.load(fp)

This way, the ultimate flexibility is attained -- the program does not impose any user constraints -- the user can use any language or tool to generate config the way he wishes.

In particular, I advise against linking with the Dhall library. While Dhall is an excellent way to generate JSON, there is never a reason for making it the only way to do so.
https://hackage.haskell.org/package/dhall