evan_tech

Previous Entry Share Next Entry
Here's an underappreciated Haskell standard library.

Text.PrettyPrint provides combinators for pretty-printing. It's kinda like a super-simplified HTML or TeX: you give it a bunch of boxes and how they relate to one another and it manages output. It's trivial to use.

A common case I have is printing something that's tree-like, where children are indented below their parents. The code for printing a node is straightforward: print this node's content, then map the print function over all its children with a bit more indentation. But the indentation bit is annoying! You have to remember to properly indent every line, and then that complicated wrapping, etc.

Instead, use the pretty-printer. A Doc is basically a box of text. text "foo" produces a Doc with that string. Then there are higher-level functions to compose Docs: nest indents a Doc, vcat puts a sequence of docs together, one above the other... and finally render converts a Doc to a string.

For my expenses chart I pretty-printed the parsed version of my expenses data.

showDays :: [DayLog] -> String
showDays days = render (vcat (map showDay days)) where
  showDay (DayLog day exps) = text (show day) <+> vcat (map showExp exps)
  showExp (Expense cost desc tags) = text (show cost) <+> text desc
                                 <+> showTags tags
  showTags tags = text "[" <> hsep (map text tags) <> text "]"

showDays outputs a sequence of days by vcatting them together.
showDay shows a single day by writing out the date on the left and then all the day's expenses vertically listed the right.
showExp and showTags just write out the fields in sequence, more or less.

The output looks like this:
2003-03-15 16 airport food [food]
2003-03-23 10 bart [transport]
           9 burger [food]
           10 ikea teapot [home]
           1 ikea water [drink]
All the expenses properly indented over on the right. (I didn't track expenses on my vacation, so there's a gap in the dates there...)