Compendium of usage patterns for Format-based pretty-printing

The Format module is quite powerful when one has to perform pretty-printing of various forms of data (programs for instance, or logical formulas…). However, the box model is not that easy to grasp in practice (despite getting easier thanks to, e.g., CCFormat or Fmt) and one (meaning: me and my colleagues) often gets messy output, with box breaks and indentation not going as expected. So I thought this thread may serve for experienced Format (or variants) programmers to show examples or patterns they use to handle common situations.

Examples of such common situations are (for me and my colleagues, at least): printing (sequences of) if/then/else expressions, handling loop constructs, printing large logical formulas (propositional logic, first-order logic…), etc. As an example, you’d expect an if/then/else to be on one line if every subexpression if short enough but to become vertical otherwise, with else being aligned w.r.t. the if in that case and subexpresions to start indented on the next line. That is:

if cbody then tbody else ebody

or

if cbody then
  tbody
else
  ebody

(where tbody and ebody may themselves be indented if they’re too long to fit one line…).

Thanks (in advance) for contributing, by adding new recurring use cases or by devising functions to achieve this!

2 Likes

After reading the Format page to find how to specify fully breaks, I got this:

# module F = CCFormat;;
# Format.printf "@[<hv2>if %a then@ %a@;<1 -2>else@ %a@]@."
  F.string "cbody" F.string "tbody------------" F.string "ebody-----------";;

which seems to do what you want. So the answer is to have some negative indentation for the else :slight_smile:

Pretty sure you don’t need negative indentation:

Format.printf "@[<hv>if %a then@;<1 2>@[%a@]@;else@;<1 2>@[%a@]@]@."

I added a few more boxes to harden against multiline content.

In general, the secret is to add lot’s of boxes everywhere. You can never go wrong if you add more boxes.
The vertical boxes are also rather handy (or, in this case, hv) by allowing to have content laid out vertically in a way that still respects indentation of the content.

The other tips is to make lot’s of individual pp function and to (ab)use %a. As long as you box everything, if pp functions behave properly in isolation, the composition will work.

1 Like

This is good advice indeed. As for the negative indentation I should have said “a solution” :slight_smile:

Thanks to both of you for your replies. This indeed seems to work well for the if/then/else construct. Still, my question is more about populating this thread with reusable examples of functions achieving common pretty-printing usage. I’m sure lots of people would be happy to see and reuse them. Fmt and CCFormat provide a combinator-like view over Format but, AFAIK, no such functions. In a past project, we used PPrint and we achieved much more easily a sensible pretty-printing for our examples (but it was a bit heavy performance-wise, hence our testing of Format). I know some argue that you can achieve the same results with Format, and I agree in principle, but in practice it’s much harder for us (said otherwise, we would love to see a PPrint-like API over Format, hiding in particular the box stuff completely (no @[ @] to write)).

I’m also curious as to why many in the community prefer using long format strings that are very error-prone and brittle rather than using combinators. I’m guessing much of this has to do with the stateful design of Format, and that had something like CCFormat been integrated into the standard library, we’d be seeing much saner pretty-printing code.

I like to have the choice actually, I still write a lot of format strings, for they are compact. Of course combinators are useful for common patterns like lists.

I just want to make sure people are aware of the existence of easy-format. It’s used by yojson’s pretty-printer and more substantially by reason-refmt to reindent Reason source code.
The interface is functional, i.e. you build a tree of the text to be printed, with style properties on each node, then print it out.
Take a look at some simple examples or more advanced ones with full source code and output.

1 Like

Actually the standard Format module may evolve in this direction. My reference is a publication about Format at JFLA 2017, by Richard Bonichon and Pierre Weis (see Format unraveled):

We are considering adding fully abstract printing (document production without I/O side effects), printing of polymorphic data structures (not to be confused with now available polymorphic printing of monomorphic values) and tables (column-formatted outputs).

We are currently experimenting with these subjects. We would like to make these extensions work
for all modules using format strings (Printf, Scanf). We are hopeful that it will provide new and
interesting ways to handle formatted data in OCaml.

1 Like