FPretty-1.1: Efficient simple pretty printing combinators

LicenseBSD3
MaintainerOlaf Chitil <O.Chitil@kent.ac.uk>
Portabilityportable
Safe HaskellSafe
LanguageHaskell98

Text.PrettyPrint.FPretty

Contents

Description

Fast pretty-printing library

A pretty printer turns a tree structure into indented text, such that the indentation reflects the tree structure. To minimise the number of lines, substructures are put on a single line as far as possible within the given line-width limit.

An pretty-printed example with 35 characters line-width:

if True
   then if True then True else True
   else
      if False 
         then False 
         else False

To obtain the above the user of a library only has to convert their tree structure into a document of type Doc.

data Exp = ETrue | EFalse | If Exp Exp Exp

toDoc :: Exp -> Doc
toDoc ETrue = text "True"
toDoc EFalse = text "False"
toDoc (If e1 e2 e3) =
  group (nest 3 (
    group (nest 3 (text "if" <> line <> toDoc e1)) <> line <>
    group (nest 3 (text "then" <> line <> toDoc e2)) <> line <>
    group (nest 3 (text "else" <> line <> toDoc e3))))

A document represents a set of layouts. The function pretty then takes a desired maximal printing width and a document and selects the layout that fits best.

Another example filling lines with elements of a list:

list2Doc :: Show a => [a] -> Doc
list2Doc xs = text "[" <> go xs <> text "]"
  where
  go [] = empty
  go [x] = text (show x)
  go (x:y:ys) = text (show x) </> text ", " <> go (y:ys)

main = putStrLn (pretty 40 (list2Doc [1..20]))

The output is

[1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10
, 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18
, 19 , 20]

FPretty is an implementation of the simple combinators designed by Phil Wadler. The library uses a single associative combinator <> to concatenate documents with empty as identity. There is a primitive document for potential line breaks, i.e., its two layouts are both a line break and a space. The group combinator then enforces that all potential line breaks within a document must be layouted in the same way, i.e. either line breaks or spaces.

The time complexity is linear in the output size. In contrast, all other pretty printing libraries (original Phil Wadler, PPrint by Leijen, Hughes / Peyton Jones) use more or less backtracking, and their speed depends unpredictably on the desired output width.

Also FPretty provides both relative and absolute indentation via nest and align, whereas HughesPJ provides only relative indentation.

FPretty uses far less space than other pretty printing libraries for large documents. It does require space linear in the nesting depth of nest/align combinators; however, having these deeply nested leads to a bad layout anyway.

Unlike other libraries, FPretty does not provide several rendering modes, but could be extended to do so.

The combinators are a subset of those of PPrint and are similar to HughesPJ to make moving from one library to the other as painless as possible.

For more implementation notes see http://www.cs.kent.ac.uk/~oc/pretty.html or Doitse Swierstra and Olaf Chitil: Linear, bounded, functional pretty-printing. Journal of Functional Programming, 19(1):1-16, January 2009.

Synopsis

The type of documents

data Doc Source

A Document represents a *set* of layouts.

Instances

Pretty printing

pretty :: Int -> Doc -> String Source

Pretty print within given width. Selects from the *set* of layouts that the document represents the widest that fits within the given width. If no such layout exists, then it will choose the narrowest that exceeds the given width.

Basic documents

empty :: Doc Source

The empty document; equal to text "".

text :: String -> Doc Source

Atomic document consisting of just the given text. There should be no newline \n in the string.

Basic documents with several layouts

line :: Doc Source

Either a space or a new line.

linebreak :: Doc Source

Either nothing (empty) or a new line.

softline :: Doc Source

A space, if the following still fits on the current line, otherwise newline.

softbreak :: Doc Source

Nothing, if the following still fits on the current line, otherwise newline.

Combining two documents

The base binary combinator

(<>) :: Doc -> Doc -> Doc infixr 6 Source

Horizontal composition of two documents. Is associative with identity empty.

Derived binary combinators

(<+>) :: Doc -> Doc -> Doc infixr 6 Source

Combine with a space in between.

(<$>) :: Doc -> Doc -> Doc infixr 5 Source

Combine with a line in between.

(<$$>) :: Doc -> Doc -> Doc infixr 5 Source

Combine with a linebreak in between.

(</>) :: Doc -> Doc -> Doc infixr 5 Source

Combine with a softline in between.

(<//>) :: Doc -> Doc -> Doc infixr 5 Source

Combine with a softbreak in between.

Modifying the layouts of one document

group :: Doc -> Doc Source

Mark document as group, that is, layout as a single line if possible. Within a group for all basic documents with several layouts the same layout is chosen, that is, they are all horizontal or all new lines. Within a vertical group there can be a horizontal group, but within a horizontal group all groups are also layouted horizontally.

nest :: Int -> Doc -> Doc Source

Increases current indentation level (absolute). Assumes argument >= 0.

align :: Doc -> Doc Source

Set indentation to current column.

hang :: Int -> Doc -> Doc Source

Increase identation relative to the *current* column.

Combining many documents

hsep :: [Doc] -> Doc Source

Combine non-empty list of documents with <+>, i.e., a space separator.

vsep :: [Doc] -> Doc Source

Combine non-empty list of documents with <$>, i.e., a line separator.

fillSep :: [Doc] -> Doc Source

Combine non-empty list of documents with </>, i.e., a softline separator.

sep :: [Doc] -> Doc Source

Combine non-empty list of documents vertically as a group. Seperated by space instead if all fit on one line.

hcat :: [Doc] -> Doc Source

Combine non-empty list of documents with <>.

vcat :: [Doc] -> Doc Source

Combine non-empty list of documents with <$$>, i.e., a linebreak separator.

fillCat :: [Doc] -> Doc Source

Combine non-empty list of documents with <//>, i.e., a softbreak separator.

cat :: [Doc] -> Doc Source

Combine non-empty list of documents, filling lines as far as possible.