By IkamusumeFan (Own work) from https://en.wikipedia.org/wiki/Monoid_(category_theory)

Having an easy-to-extend pattern for configuration is important to keep an application clean and well-structured. The Partial Options Monoid pattern does just that.

The Problem

Let’s say you have a program and it has gotten to the point where just reading strings from the command line is not cutting it:

You want to design a parse phase for your program that will create a nice Options type:

We would like to be able to create multiple versions of this record and combine them. For instance, we want to read arguments from the command line, but we also have defaults. In the future, we will even want read options from a file. Instead of doing this directly, we will create a separate record: the PartialOptions record.

Step 1: Make the Partial Options Type

The first step is to make a record similar to Options, but every field will be wrapped in a monoid, in this case Last.

We can then make a Monoid instance for our PartialOptions type:

We will use mappend or <> to combine different configs.

Step 2: Convert the Partial Options to an Options

The PartialOptions are convenient because we can combine them. However, it is not the type we need to run our program. We need to convert from a PartialOptions to an Options. This process will fail if we have not specified all the options:

Step 3: Make the Default Options

The first PartialOptions we will make are the defaults:

The important thing to notice is we have not specified the poHost option. This means it is a required option. If it is not specified somehow, the makeOptions function will fail.

Step 4: Write a Parser

I’ll give an example of making a command line parser with optparse-applicative.

We are being careful to make sure there is a way we can unset the poCharacterCode option or specify a new value.

Step 5: Combine the Partials

We can now parse and combine the results with our defaults:

Our program will use a strongly typed Options type:

So our main becomes:

Step Later: Write another Parser

At some point later, we could decide we want to also read options from a config file. Let’s write the code to read a YAML config file, assuming one exists:

We must also extend the parseOptions function:

Set Yourself Up For Success

Parsing options is not the hardest problem. However, if you do not create a pattern the rest of your team can follow, your program can become a tangled mess of random file reads, environment variable lookups and unpredictable defaulting.

The Monoid class is a rock solid abstraction for combining options. You can start simple with a single configuration method, and later add additional methods at any time.

Starting with PartialOptions pattern early on is a great way to keep your application clean.

If you’re curious take a look at https://github.com/jfischoff/partial-options-monoid-pattern for a demo project that includes environment variable parsing.

Haskeller Haskelling.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store