Paolo Capriotti's blog

Type theory, category theory, functional programming

Applicative option parser

There are quite a few option parsing libraries on Hackage already, but they either depend on Template Haskell, or require some boilerplate. Although I have nothing against the use of Template Haskell in general, I’ve always found its use in this case particularly unsatisfactory, and I’m convinced that a more idiomatic solution should exist.

In this post, I present a proof of concept implementation of a library that allows you to define type-safe option parsers in Applicative style.

The only extension that we actually need is GADT, since, as will be clear in a moment, our definition of Parser requires existential quantification.

Let’s start by defining the Option type, corresponding to a concrete parser for a single option:

For simplicity, we only support “long” options with exactly 1 argument. The optMatches function checks if an option matches a string given on the command line.

We can now define the main Parser type:

The Parser GADT resembles a heterogeneous list, with two constructors.

The NilP r constructor represents a “null” parser that doesn’t consume any arguments, and always returns r as a result.

The ConsP constructor is the combination of an Option returning a function, and an arbitrary parser returning an argument for that function. The combined parser applies the function to the argument and returns a result.

The definition of (<*>) probably needs some clarification. The variables involved have types:

and we want to obtain a parser of type Parser c. So we uncurry the option, obtaining:

and compose it with a parser for the (a, b) pair, obtained by applying the (<*>) operator recursively:

This is already enough to define some example parsers. Let’s first add a couple of convenience functions to help us create basic parsers:

And a record to contain the result of our parser:

A parser for User is easily defined in applicative style:

To be able to actually use this parser, we need a “run” function:

The idea is very simple: we take the first argument, and we go over each option of the parser, check if it matches, and if it does, we replace it with a NilP parser wrapping the result, consume the option and its argument from the argument list, then call runParser recursively.

Here is an example of runParser in action:

The order of arguments doesn’t matter:

Missing arguments will result in a parse error (i.e. Nothing). We don’t support default values but they are pretty easy to add.

I think the above Parser type represents a pretty clean and elegant solution to the option parsing problem. To make it actually usable, I would need to add a few more features (boolean flags, default values, a help generator) and improve error handling and performance (right now parsing a single option is quadratic in the size of the Parser), but it looks like a fun project.

Does anyone think it’s worth adding yet another option parser to Hackage?

Comments