This post is about the formatting package.
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
It’s a continuation-based way of building up monoidal functions by composition with the ability to insert constants in-between. Example:
now function inserts a monoidal value directly into the composition. So
is equivalent to
is equivalent to
The package is available on Hackage as formatting.
or with short-names:
Similar to C’s
and Common Lisp’s
This is my version of the
HoleyMonoid. To make this into a useful package I changed a few things.
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
There is an array of top-level printing functions for various output types:
-- | Run the formatter and return a lazy 'Text' value. format :: Holey Builder Text a -> a -- | Run the formatter and return a strict 'S.Text' value. sformat :: Holey Builder S.Text a -> a -- | Run the formatter and return a 'Builder' value. bprint :: Holey Builder Builder a -> a -- | Run the formatter and print out the text to stdout. fprint :: Holey Builder (IO ()) a -> a -- | Run the formatter and put the output onto the given 'Handle'. hprint :: Handle -> Holey Builder (IO ()) a -> a
All the combinators work on a lazy text Builder which has good appending complexity and can output to a handle in chunks.
There is a short-hand type for any formatter:
All formatters are written in terms of
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
For which there are a bunch of instances. See the README for a full set of examples.
%. is like
% but feeds one formatter into another:
You can include things verbatim in the formatter:
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
The function you format with (
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
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:
mint is a formatter to show
Although a better, more general combinator might be:
Now you can use it to maybe format things:
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.