Pretty Printing a Tree with Text.PrettyPrint

I looked into Haskell’s pretty package (and the prettyclass package) to print out a simple tree structure I defined. I wanted a nicely nested output.

Why not Show?

The show typeclass isn’t what we want for human readable output. read . show should be the same as id. This means that we can’t ever throw away extraneous data , and we have to be 100% sure to preserve any structure.

These rules get in the way of human readable output. For example, printing a User record might omit the full address if it gets too long, not include the bio, and generally lay out the data in a simplified manner.

The Pretty typeclass

The prettyclass package defines a general typeclass for all types that can be printed out for human consumption. It comes with some standard types defined (like Int and similar)

Before we talk about pretty printing, we need to look at our type first.

data Tree a = Leaf a | Node (Tree a) (Tree a) deriving (Show)

A simple binary tree, where interior nodes don’t store anything, and leaves hold all the values.

We need to define an instance of our Pretty for our type. As the docs say, Minimal complete definition is either pPrintPrec or pPrint.

pPrintPrec takes a PrettyLevel, which defines the level of detailed requested. Since we want to show all the data in the tree, this is unnecessary for us, we can implement the simpler pPrint function.

The typeclass instance definition

Since our type is parameterized on a, we need to limit it somehow. In this case, we’re going to say that if our a is a member of Pretty , then we can pretty print a whole tree of a

import Text.PrettyPrint.HughesPJClass

instance (Pretty a) => Pretty (Tree a) where
  pPrint tree = ...

The Text.PrettyPrint

The pretty package implements a TON of helpers to actually lay out the data.

In this case, our goal is to have the tree look like:

Node:
  Leaf: 1
  Node:
    Leaf: 2
    Leaf: 3

Each level gets labeled by type, and nesting levels get indented by 2.

The Leaf Case

So first, lets do the Leaf case, where we print the literal Leaf: and then ask the a type to be pretty printed itself.

  pPrint (Leaf a)   = text "Leaf: " <> pPrint a

Pretty implements <> the same as a monoid, combining two Docs into one. Pretty also implements a <+> which is like <> except it will insert a space between two non-empty documents.

The Node case

The Node case is much more interesting.

  pPrint (Node l r) = vcat [ text "Node:"
                           , nest 2 (pPrint l)
                           , nest 2 (pPrint r)]

First we destructure the argument , then we build a 3 element list , each containing a Doc type. The first one is the literal Node: text , then the next two are indented by 2 spaces. Then we recursively call pPrint on the left and right sub-trees.

nest takes an indent level, and a document and returns a new document with the same content, except indented.

The vcat function takes a list of documents, and lays them out vertically.

Fairly straight forward.

And…

I was impressed by how easy this library was to use. Although I was rather confused by how hard it was to use the typeclass. The pretty package specifically has a module that defines the Pretty class, but GHC couldn’t find it.

I could see the use of this library in a large project, full of custom types.

A logging function could easily ask to prettyShow each individual item it logs.

Full code is available at: https://github.com/cschneid/cschneid-pretty/