Formatting in Haskell

This post is about the formatting package.

What’s wrong with printf?

The Text.Printf module is problematic simply because it’s not type-safe:

And it’s not extensible in the argument type. The PrintfType class does not export its methods.

And it’s not extensible in the formatting. You can’t add a “%K” syntax to it to print a value in Kelvins, for example.

And it’s implicit. You can’t just use your normal API searching facilities to search how to print a Day .

Holy Moly!

A while ago I was inspired by the HoleyMonoid package to use that mechanism to make a general replacement for printf .

It’s a continuation-based way of building up monoidal functions by composition with the ability to insert constants in-between. Example:

The now function inserts a monoidal value directly into the composition. So

is equivalent to

And

is equivalent to

The Formatting package

The package is available on Hackage as formatting.

Comparison with other formatters

Example:

or with short-names:

Similar to C’s printf :

and Common Lisp’s FORMAT :

The Holey type

This is my version of the HoleyMonoid . To make this into a useful package I changed a few things.

The Category instance implied a name conflict burden with (.) , so I changed that to (%) :

Rather than have the name-conflicting map function, I flipped the type arguments of the type and made it an instance of Functor .

Printers

There is an array of top-level printing functions for various output types:

All the combinators work on a lazy text Builder which has good appending complexity and can output to a handle in chunks.

The Format type

There is a short-hand type for any formatter:

All formatters are written in terms of now or later .

Formatters

There is a standard set of formatters in Formatting.Formatters, for example:

Finally, there is a general build function that will build anything that is an instance of the Build class from the text-format package:

For which there are a bunch of instances. See the README for a full set of examples.

Composing formatters

%. is like % but feeds one formatter into another:

Extension

You can include things verbatim in the formatter:

Although with OverloadedStrings you can just use string literals:

You can handle things later which makes the formatter accept arguments:

The type of the function passed to later should return an instance of Monoid .

The function you format with ( format , bprint , etc.) will determine the monoid of choice. In the case of this library, the top-level formating functions expect you to build a text Builder :

Because builders are efficient generators.

So in this case we will be expected to produce Builders from arguments:

To do that for common types you can just re-use the formatting library and use bprint:

Coming back to later , we can now use it to build our own printer combinators:

Now mint is a formatter to show Maybe Integer :

Although a better, more general combinator might be:

Now you can use it to maybe format things:

Retrospective

I’ve been using formatting in a bunch of projects since writing it. Happily, its API has been stable since releasing with some additions.

It has the same advantages as Parsec . It’s a combinator-based mini-language with all the same benefits.

© 2014-09-20 Chris Done