HORIZON HASKELLDocslts/ghc-9.10.x248f8f02026-10-05Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · 248f8f0 · 2026-10-05

Moduleprettyprinter-1.7.1Haskell2010

Prettyprinter

Overview

This module defines a prettyprinter to format text in a flexible and convenient way. The idea is to combine a Document out of many small components, then using a layouter to convert it to an easily renderable SimpleDocStream, which can then be rendered to a variety of formats, for example plain Text.

The documentation consists of several parts:

  1. Just below is some general information about the library.

  2. The actual library with extensive documentation and examples

  3. Migration guide for users familiar with (ansi-)wl-pprint

Starting out

As a reading list for starters, some of the most commonly used functions in this module include <>, hsep, <+>, vsep, align, hang. These cover many use cases already, and many other functions are variations or combinations of these.

Simple example

Let’s prettyprint a simple Haskell type definition. First, intersperse -> and add a leading ::,

Example4 expressions
:{prettyprintType :: [Doc x] -> Doc xprettyprintType = align . sep . zipWith (<+>) ("::" : repeat "->"):}

The sep function is one way of concatenating documents, there are multiple others, e.g. vsep, cat and fillSep. In our case, sep space-separates all entries if there is space, and newlines if the remaining line is too short.

Second, prepend the name to the type,

Example1 expression
let prettyprintDeclaration n tys = pretty n <+> prettyprintType tys

Now we can define a document that contains some type signature:

Example1 expression
let doc = prettyprintDeclaration "example" ["Int", "Bool", "Char", "IO ()"]

This document can now be printed, and it automatically adapts to available space. If the page is wide enough (80 characters in this case), the definitions are space-separated,

Example1 expression
putDocW 80 docexample :: Int -> Bool -> Char -> IO ()

If we narrow the page width to only 20 characters, the same document renders vertically aligned:

Example1 expression
putDocW 20 docexample :: Int        -> Bool        -> Char        -> IO ()

Speaking of alignment, had we not used align, the -> would be at the beginning of each line, and not beneath the ::.

The putDocW renderer used here is from Prettyprinter.Util.

General workflow

╔══════════╗
║          ║                         ╭────────────────────╮
║          ║                         │ vsep, pretty, <+>, │
║          ║                         │ nest, align, …     │
║          ║                         ╰─────────┬──────────╯
║          ║                                   │
║  Create  ║                                   │
║          ║                                   │
║          ║                                   ▽
║          ║                         ╭───────────────────╮
║          ║                         │        Doc        │
╠══════════╣                         │  (rich document)  │
║          ║                         ╰─────────┬─────────╯
║          ║                                   │
║          ║                                   │ Layout algorithms
║  Layout  ║                                   │ e.g. layoutPretty
║          ║                                   ▽
║          ║                         ╭───────────────────╮
║          ║                         │  SimpleDocStream  │
╠══════════╣                         │ (simple document) │
║          ║                         ╰─────────┬─────────╯
║          ║                                   │
║          ║                                   ├─────────────────────────────╮
║          ║                                   │                             │ treeForm
║          ║                                   │                             ▽
║          ║                                   │                     ╭───────────────╮
║          ║                                   │                     │ SimpleDocTree │
║  Render  ║                                   │                     ╰───────┬───────╯
║          ║                                   │                             │
║          ║               ╭───────────────────┼─────────────────╮  ╭────────┴────────╮
║          ║               │                   │                 │  │                 │
║          ║               ▽                   ▽                 ▽  ▽                 ▽
║          ║       ╭───────────────╮   ╭───────────────╮   ╭───────────────╮   ╭───────────────╮
║          ║       │ ANSI terminal │   │  Plain Text   │   │ other/custom  │   │     HTML      │
║          ║       ╰───────────────╯   ╰───────────────╯   ╰───────────────╯   ╰───────────────╯
║          ║
╚══════════╝

How the layout works

There are two key concepts to laying a document out: the available width, and grouping.

Available width

The page has a certain maximum width, which the layouter tries to not exceed, by inserting line breaks where possible. The functions given in this module make it fairly straightforward to specify where, and under what circumstances, such a line break may be inserted by the layouter, for example via the sep function.

There is also the concept of ribbon width. The ribbon is the part of a line that is printed, i.e. the line length without the leading indentation. The layouters take a ribbon fraction argument, which specifies how much of a line should be filled before trying to break it up. A ribbon width of 0.5 in a document of width 80 will result in the layouter to try to not exceed 0.5*80 = 40 (ignoring current indentation depth).

Grouping

A document can be grouped, which tells the layouter that it should attempt to collapse it to a single line. If the result does not fit within the constraints (given by page and ribbon widths), the document is rendered unaltered. This allows fallback definitions, so that we get nice results even when the original document would exceed the layout constraints.

Things the prettyprinter cannot do

Due to how the Wadler/Leijen algorithm is designed, a couple of things are unsupported right now, with a high possibility of having no sensible implementation without significantly changing the layout algorithm. In particular, this includes

  • Leading symbols instead of just spaces for indentation, as used by the Linux tree tool for example

  • Multi-column layouts, in particular tables with multiple cells of equal width adjacent to each other

Some helpful tips

Which kind of annotation should I use?

Summary: Use semantic annotations for Doc, and after layouting map to backend-specific ones.

For example, suppose you want to prettyprint some programming language code. If you want keywords to be red, you should annotate the Doc with a type that has a Keyword field (without any notion of color), and then after layouting convert the annotations to map Keyword to e.g. Red (using reAnnotateS). The alternative that I do not recommend is directly annotating the Doc with Red.

While both versions would superficially work equally well and would create identical output, the recommended way has two significant advantages: modularity and extensibility.

Modularity: To change the color of keywords later, you have to touch one point, namely the mapping in reAnnotateS, where Keyword is mapped to Red. If you have 'annotate Red …' everywher, you’ll have to do a full text replacement, producing a large diff and touching lots of places for a very small change.

Extensibility: Adding a different backend in the recommended version is simply adding another reAnnotateS to convert the Doc annotation to something else. On the other hand, if you have Red as an annotation in the Doc already and the other backend does not support anything red (think of plain text or a website where red doesn’t work well with the rest of the style), you’ll have to worry about what to map »redness« to, which has no canonical answer. Should it be omitted? What does »red« mean anyway – maybe keywords and variables are red, and you want to change only the color of variables?

  • 5 types
  • 1 class
  • 75 values
  • Packageprettyprinter-1.7.1
  • Exports82
  • LanguageHaskell2010
  • LicenceBSD-2-Clause
  • SourcePrettyprinter.hs

Documents

1 declaration
datadata Doc ann
#

The abstract data type Doc ann represents pretty documents that have been annotated with data of type ann.

More specifically, a value of type Doc represents a non-empty set of possible layouts of a document. The layout functions select one of these possibilities, taking into account things like the width of the output document.

The annotation is an arbitrary piece of data associated with (part of) a document. Annotations may be used by the rendering backends in order to display output differently, such as

  • color information (e.g. when rendering to the terminal)

  • mouseover text (e.g. when rendering to rich HTML)

  • whether to show something or not (to allow simple or detailed versions)

The simplest way to display a Doc is via the Show class.

Example1 expression
putStrLn (show (vsep ["hello", "world"]))helloworld
Instances7Functor, Show, IsString, Generic, Semigroup, Monoid, …

Basic functionality

10 declarations
classclass Pretty a where
#

Overloaded conversion to Doc.

Laws:

  1. output should be pretty. :-)

Methods

  • pretty :: a -> Doc ann
    Example1 expression
    pretty 1 <+> pretty "hello" <+> pretty 1.2341 hello 1.234
  • prettyList :: [a] -> Doc ann

    prettyList is only used to define the instance Pretty a => Pretty [a]. In normal circumstances only the pretty function is used.

    Example1 expression
    prettyList [1, 23, 456][1, 23, 456]
Instances27Pretty, …
  • Pretty IntegerDefined in prettyprinter-1.7.1 · Prettyprinter.Internal
    Example1 expression
    pretty (2^123 :: Integer)10633823966279326983230456482242756608
  • Pretty NaturalDefined in prettyprinter-1.7.1 · Prettyprinter.Internal
  • Pretty VoidDefined in prettyprinter-1.7.1 · Prettyprinter.Internal

    Finding a good example for printing something that does not exist is hard, so here is an example of printing a list full of nothing.

    Example1 expression
    pretty ([] :: [Void])[]
  • Pretty Int16Defined in prettyprinter-1.7.1 · Prettyprinter.Internal
  • Pretty Int32Defined in prettyprinter-1.7.1 · Prettyprinter.Internal
  • Pretty Int64Defined in prettyprinter-1.7.1 · Prettyprinter.Internal
  • Pretty Int8Defined in prettyprinter-1.7.1 · Prettyprinter.Internal
  • Pretty Word16Defined in prettyprinter-1.7.1 · Prettyprinter.Internal
  • Pretty Word32Defined in prettyprinter-1.7.1 · Prettyprinter.Internal
  • Pretty Word64Defined in prettyprinter-1.7.1 · Prettyprinter.Internal
  • Pretty Word8Defined in prettyprinter-1.7.1 · Prettyprinter.Internal
  • Pretty BoolDefined in prettyprinter-1.7.1 · Prettyprinter.Internal
    Example1 expression
    pretty TrueTrue
  • Pretty CharDefined in prettyprinter-1.7.1 · Prettyprinter.Internal

    Instead of (pretty 'n'), consider using line as a more readable alternative.

    Example2 expressions
    pretty 'f' <> pretty 'o' <> pretty 'o'foopretty ("string" :: String)string
  • Pretty DoubleDefined in prettyprinter-1.7.1 · Prettyprinter.Internal
    Example1 expression
    pretty (exp 1 :: Double)2.71828182845904...
  • Pretty FloatDefined in prettyprinter-1.7.1 · Prettyprinter.Internal
    Example1 expression
    pretty (pi :: Float)3.1415927
  • Pretty IntDefined in prettyprinter-1.7.1 · Prettyprinter.Internal
    Example1 expression
    pretty (123 :: Int)123
  • Pretty WordDefined in prettyprinter-1.7.1 · Prettyprinter.Internal
  • Pretty TextDefined in prettyprinter-1.7.1 · Prettyprinter.Internal

    Automatically converts all newlines to line.

    Example1 expression
    pretty ("hello\nworld" :: Text)helloworld

    Note that line can be undone by group:

    Example1 expression
    group (pretty ("hello\nworld" :: Text))hello world

    Manually use hardline if you definitely want newlines.

  • Pretty TextDefined in prettyprinter-1.7.1 · Prettyprinter.Internal

    (lazy Text instance, identical to the strict version)

  • Pretty ()Defined in prettyprinter-1.7.1 · Prettyprinter.Internal
    Example1 expression
    pretty ()()

    The argument is not used:

    Example1 expression
    pretty (error "Strict?" :: ())()
  • Pretty a => Pretty (NonEmpty a)Defined in prettyprinter-1.7.1 · Prettyprinter.Internal
  • Pretty a => Pretty (Identity a)Defined in prettyprinter-1.7.1 · Prettyprinter.Internal
    Example1 expression
    pretty (Identity 1)1
  • Pretty a => Pretty (Maybe a)Defined in prettyprinter-1.7.1 · Prettyprinter.Internal

    Ignore Nothings, print Just contents.

    Example2 expressions
    pretty (Just True)Truebraces (pretty (Nothing :: Maybe Bool)){}
    Example1 expression
    pretty [Just 1, Nothing, Just 3, Nothing][1, 3]
  • Pretty a => Pretty [a]Defined in prettyprinter-1.7.1 · Prettyprinter.Internal
    Example1 expression
    pretty [1,2,3][1, 2, 3]
  • (Pretty a1, Pretty a2) => Pretty (a1, a2)Defined in prettyprinter-1.7.1 · Prettyprinter.Internal
    Example1 expression
    pretty (123, "hello")(123, hello)
  • Pretty a => Pretty (Const a b)Defined in prettyprinter-1.7.1 · Prettyprinter.Internal
  • (Pretty a1, Pretty a2, Pretty a3) => Pretty (a1, a2, a3)Defined in prettyprinter-1.7.1 · Prettyprinter.Internal
    Example1 expression
    pretty (123, "hello", False)(123, hello, False)
valueunsafeViaShow :: Show a => a -> Doc ann
#

Convenience function to convert a Showable value /that must not contain newlines/ to a Doc. If there may be newlines, use viaShow instead.

valueemptyDoc :: Doc ann
#

The empty document behaves like (pretty ""), so it has a height of 1. This may lead to surprising behaviour if we expect it to bear no weight inside e.g. vcat, where we get an empty line of output from it (parens for visibility only):

Example1 expression
vsep ["hello", parens emptyDoc, "world"]hello()world

Together with <>, emptyDoc forms the Monoid Doc.

valuenest
  1. :: Int

    Change of nesting level

  2. -> Doc ann
  3. -> Doc ann
#

(nest i x) lays out the document x with the current nesting level (indentation of the following lines) increased by i. Negative values are allowed, and decrease the nesting level accordingly.

Example1 expression
vsep [nest 4 (vsep ["lorem", "ipsum", "dolor"]), "sit", "amet"]lorem    ipsum    dolorsitamet

See also

  • hang (nest relative to current cursor position instead of current nesting level)

  • align (set nesting level to current cursor position)

  • indent (increase indentation on the spot, padding with spaces).

valueline :: Doc ann
#

The line document advances to the next line and indents to the current nesting level.

Example2 expressions
let doc = "lorem ipsum" <> line <> "dolor sit amet"doclorem ipsumdolor sit amet

line behaves like space if the line break is undone by group:

Example1 expression
group doclorem ipsum dolor sit amet
valueline' :: Doc ann
#

line' is like line, but behaves like mempty if the line break is undone by group (instead of space).

Example3 expressions
let doc = "lorem ipsum" <> line' <> "dolor sit amet"doclorem ipsumdolor sit ametgroup doclorem ipsumdolor sit amet
valuesoftline :: Doc ann
#

softline behaves like space if the resulting output fits the page, otherwise like line.

Here, we have enough space to put everything in one line:

Example2 expressions
let doc = "lorem ipsum" <> softline <> "dolor sit amet"putDocW 80 doclorem ipsum dolor sit amet

If we narrow the page to width 10, the layouter produces a line break:

Example1 expression
putDocW 10 doclorem ipsumdolor sit amet
softline = group line
valuesoftline' :: Doc ann
#

softline' is like softline, but behaves like mempty if the resulting output does not fit on the page (instead of space). In other words, line is to line' how softline is to softline'.

With enough space, we get direct concatenation:

Example2 expressions
let doc = "ThisWord" <> softline' <> "IsWayTooLong"putDocW 80 docThisWordIsWayTooLong

If we narrow the page to width 10, the layouter produces a line break:

Example1 expression
putDocW 10 docThisWordIsWayTooLong
softline' = group line'
valuehardline :: Doc ann
#

A hardline is always laid out as a line break, even when grouped or when there is plenty of space. Note that it might still be simply discarded if it is part of a flatAlt inside a group.

Example2 expressions
let doc = "lorem ipsum" <> hardline <> "dolor sit amet"putDocW 1000 doclorem ipsumdolor sit amet
Example1 expression
group doclorem ipsumdolor sit amet

Primitives for alternative layouts

valuegroup :: Doc ann -> Doc ann
#

(group x) tries laying out x into a single line by removing the contained line breaks; if this does not fit the page, or when a hardline within x prevents it from being flattened, x is laid out without any changes.

The group function is key to layouts that adapt to available space nicely.

See vcat, line, or flatAlt for examples that are related, or make good use of it.

valueflatAlt
  1. :: Doc ann

    Default

  2. -> Doc ann

    Preferred when grouped

  3. -> Doc ann
#

By default, (flatAlt x y) renders as x. However when grouped, y will be preferred, with x as the fallback for the case when y doesn't fit.

Example4 expressions
let doc = flatAlt "a" "b"putDoc docaputDoc (group doc)bputDocW 0 (group doc)a

flatAlt is particularly useful for defining conditional separators such as

softline = group (flatAlt hardline " ")
Example3 expressions
let hello = "Hello" <> softline <> "world!"putDocW 12 helloHello world!putDocW 11 helloHelloworld!
Example: Haskell's do-notation

We can use this to render Haskell's do-notation nicely:

Example5 expressions
let open        = flatAlt "" "{ "let close       = flatAlt "" " }"let separator   = flatAlt "" "; "let prettyDo xs = group ("do" <+> align (encloseSep open close separator xs))let statements  = ["name:_ <- getArgs", "let greet = \"Hello, \" <> name", "putStrLn greet"]

This is put into a single line with {;} style if it fits:

Example1 expression
putDocW 80 (prettyDo statements)do { name:_ <- getArgs; let greet = "Hello, " <> name; putStrLn greet }

When there is not enough space the statements are broken up into lines nicely:

Example1 expression
putDocW 10 (prettyDo statements)do name:_ <- getArgs   let greet = "Hello, " <> name   putStrLn greet
Notes

Users should be careful to choose x to be less wide than y. Otherwise, if y turns out not to fit the page, we fall back on an even wider layout:

Example2 expressions
let ugly = group (flatAlt "even wider" "too wide")putDocW 7 uglyeven wider

Also note that group will flatten y:

Example1 expression
putDoc (group (flatAlt "x" ("y" <> line <> "y")))y y

This also means that an "unflattenable" y which contains a hard linebreak will never be rendered:

Example1 expression
putDoc (group (flatAlt "x" ("y" <> hardline <> "y")))x

Alignment functions

6 declarations

The functions in this section cannot be described by Wadler's original functions. They align their output relative to the current output position - in contrast to nest which always aligns to the current nesting level. This deprives these functions from being 'optimal'. In practice however they prove to be very useful. The functions in this section should be used with care, since they are more expensive than the other functions. For example, align shouldn't be used to pretty print all top-level declarations of a language, but using hang for let expressions is fine.

valuealign :: Doc ann -> Doc ann
#

(align x) lays out the document x with the nesting level set to the current column. It is used for example to implement hang.

As an example, we will put a document right above another one, regardless of the current nesting level. Without alignment, the second line is put simply below everything we've had so far:

Example1 expression
"lorem" <+> vsep ["ipsum", "dolor"]lorem ipsumdolor

If we add an align to the mix, the vsep's contents all start in the same column:

Example1 expression
"lorem" <+> align (vsep ["ipsum", "dolor"])lorem ipsum      dolor
valuehang
  1. :: Int

    Change of nesting level, relative to the start of the first line

  2. -> Doc ann
  3. -> Doc ann
#

(hang i x) lays out the document x with a nesting level set to the current column plus i. Negative values are allowed, and decrease the nesting level accordingly.

Example2 expressions
let doc = reflow "Indenting these words with hang"putDocW 24 ("prefix" <+> hang 4 doc)prefix Indenting these           words with           hang

This differs from nest, which is based on the current nesting level plus i. When you're not sure, try the more efficient nest first. In our example, this would yield

Example2 expressions
let doc = reflow "Indenting these words with nest"putDocW 24 ("prefix" <+> nest 4 doc)prefix Indenting these    words with nest
hang i doc = align (nest i doc)
valueindent
  1. :: Int

    Number of spaces to increase indentation by

  2. -> Doc ann
  3. -> Doc ann
#

(indent i x) indents document x by i columns, starting from the current cursor position.

Example2 expressions
let doc = reflow "The indent function indents these words!"putDocW 24 ("prefix" <> indent 4 doc)prefix    The indent          function          indents these          words!
indent i d = hang i ({i spaces} <> d)
valueencloseSep
  1. :: Doc ann

    left delimiter

  2. -> Doc ann

    right delimiter

  3. -> Doc ann

    separator

  4. -> [Doc ann]

    input documents

  5. -> Doc ann
#

(encloseSep l r sep xs) concatenates the documents xs separated by sep, and encloses the resulting document by l and r.

The documents are laid out horizontally if that fits the page:

Example2 expressions
let doc = "list" <+> align (encloseSep lbracket rbracket comma (map pretty [1,20,300,4000]))putDocW 80 doclist [1,20,300,4000]

If there is not enough space, then the input is split into lines entry-wise therwise they are laid out vertically, with separators put in the front:

Example1 expression
putDocW 10 doclist [1     ,20     ,300     ,4000]

Note that doc contains an explicit call to align so that the list items are aligned vertically.

For putting separators at the end of entries instead, have a look at punctuate.

valuelist :: [Doc ann] -> Doc ann
#

Haskell-inspired variant of encloseSep with braces and comma as separator.

Example1 expression
let doc = list (map pretty [1,20,300,4000])
Example1 expression
putDocW 80 doc[1, 20, 300, 4000]
Example1 expression
putDocW 10 doc[ 1, 20, 300, 4000 ]
valuetupled :: [Doc ann] -> Doc ann
#

Haskell-inspired variant of encloseSep with parentheses and comma as separator.

Example1 expression
let doc = tupled (map pretty [1,20,300,4000])
Example1 expression
putDocW 80 doc(1, 20, 300, 4000)
Example1 expression
putDocW 10 doc( 1, 20, 300, 4000 )

Binary functions

2 declarations
method(<>) :: a -> a -> a
#

An associative operation.

Examples
Example1 expression
[1,2,3] <> [4,5,6][1,2,3,4,5,6]
Example1 expression
Just [1, 2, 3] <> Just [4, 5, 6]Just [1,2,3,4,5,6]
Example1 expression
putStr "Hello, " <> putStrLn "World!"Hello, World!
value(<+>) :: Doc ann -> Doc ann -> Doc ann
#

(x <+> y) concatenates document x and y with a space in between.

Example1 expression
"hello" <+> "world"hello world
x <+> y = x <> space <> y

List functions

1 declaration

The sep and cat functions differ in one detail: when grouped, the seps replace newlines wich spaces, while the cats simply remove them. If you're not sure what you want, start with the seps.

valueconcatWith
  1. :: Foldable t
  2. => Doc ann -> Doc ann -> Doc ann
  3. -> t (Doc ann)
  4. -> Doc ann
#

Concatenate all documents element-wise with a binary function.

concatWith _ [] = mempty
concatWith (**) [x,y,z] = x ** y ** z

Multiple convenience definitions based on concatWith are already predefined, for example:

hsep    = concatWith (<+>)
fillSep = concatWith (\x y -> x <> softline <> y)

This is also useful to define customized joiners:

Example1 expression
concatWith (surround dot) ["Prettyprinter", "Render", "Text"]Prettyprinter.Render.Text

sep family

When grouped, these will replace newlines with spaces.

valuehsep :: [Doc ann] -> Doc ann
#

(hsep xs) concatenates all documents xs horizontally with <+>, i.e. it puts a space between all entries.

Example1 expression
let docs = Util.words "lorem ipsum dolor sit amet"
Example1 expression
hsep docslorem ipsum dolor sit amet

hsep does not introduce line breaks on its own, even when the page is too narrow:

Example1 expression
putDocW 5 (hsep docs)lorem ipsum dolor sit amet

For automatic line breaks, consider using fillSep instead.

valuevsep :: [Doc ann] -> Doc ann
#

(vsep xs) concatenates all documents xs above each other. If a group undoes the line breaks inserted by vsep, the documents are separated with a space instead.

Using vsep alone yields

Example1 expression
"prefix" <+> vsep ["text", "to", "lay", "out"]prefix texttolayout

grouping a vsep separates the documents with a space if it fits the page (and does nothing otherwise). See the sep convenience function for this use case.

The align function can be used to align the documents under their first element:

Example1 expression
"prefix" <+> align (vsep ["text", "to", "lay", "out"])prefix text       to       lay       out

Since grouping a vsep is rather common, sep is a built-in for doing that.

valuefillSep :: [Doc ann] -> Doc ann
#

(fillSep xs) concatenates the documents xs horizontally with <+> as long as it fits the page, then inserts a line and continues doing that for all documents in xs. (line means that if grouped, the documents are separated with a space instead of newlines. Use fillCat if you do not want a space.)

Let's print some words to fill the line:

Example2 expressions
let docs = take 20 (cycle ["lorem", "ipsum", "dolor", "sit", "amet"])putDocW 80 ("Docs:" <+> fillSep docs)Docs: lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolorsit amet lorem ipsum dolor sit amet

The same document, printed at a width of only 40, yields

Example1 expression
putDocW 40 ("Docs:" <+> fillSep docs)Docs: lorem ipsum dolor sit amet loremipsum dolor sit amet lorem ipsum dolorsit amet lorem ipsum dolor sit amet
valuesep :: [Doc ann] -> Doc ann
#

(sep xs) tries laying out the documents xs separated with spaces, and if this does not fit the page, separates them with newlines. This is what differentiates it from vsep, which always lays out its contents beneath each other.

Example2 expressions
let doc = "prefix" <+> sep ["text", "to", "lay", "out"]putDocW 80 docprefix text to lay out

With a narrower layout, the entries are separated by newlines:

Example1 expression
putDocW 20 docprefix texttolayout
sep = group . vsep

cat family

When grouped, these will remove newlines.

valuehcat :: [Doc ann] -> Doc ann
#

(hcat xs) concatenates all documents xs horizontally with <> (i.e. without any spacing).

It is provided only for consistency, since it is identical to mconcat.

Example2 expressions
let docs = Util.words "lorem ipsum dolor"hcat docsloremipsumdolor
valuevcat :: [Doc ann] -> Doc ann
#

(vcat xs) vertically concatenates the documents xs. If it is grouped, the line breaks are removed.

In other words vcat is like vsep, with newlines removed instead of replaced by spaces.

Example3 expressions
let docs = Util.words "lorem ipsum dolor"vcat docsloremipsumdolorgroup (vcat docs)loremipsumdolor

Since grouping a vcat is rather common, cat is a built-in shortcut for it.

valuefillCat :: [Doc ann] -> Doc ann
#

(fillCat xs) concatenates documents xs horizontally with <> as long as it fits the page, then inserts a line' and continues doing that for all documents in xs. This is similar to how an ordinary word processor lays out the text if you just keep typing after you hit the maximum line length.

(line' means that if grouped, the documents are separated with nothing instead of newlines. See fillSep if you want a space instead.)

Observe the difference between fillSep and fillCat. fillSep concatenates the entries spaced when grouped:

Example2 expressions
let docs = take 20 (cycle (["lorem", "ipsum", "dolor", "sit", "amet"]))putDocW 40 ("Grouped:" <+> group (fillSep docs))Grouped: lorem ipsum dolor sit ametlorem ipsum dolor sit amet lorem ipsumdolor sit amet lorem ipsum dolor sitamet

On the other hand, fillCat concatenates the entries directly when grouped:

Example1 expression
putDocW 40 ("Grouped:" <+> group (fillCat docs))Grouped: loremipsumdolorsitametloremipsumdolorsitametloremipsumdolorsitametloremipsumdolorsitamet
valuecat :: [Doc ann] -> Doc ann
#

(cat xs) tries laying out the documents xs separated with nothing, and if this does not fit the page, separates them with newlines. This is what differentiates it from vcat, which always lays out its contents beneath each other.

Example2 expressions
let docs = Util.words "lorem ipsum dolor"putDocW 80 ("Docs:" <+> cat docs)Docs: loremipsumdolor

When there is enough space, the documents are put above one another:

Example1 expression
putDocW 10 ("Docs:" <+> cat docs)Docs: loremipsumdolor
cat = group . vcat

Others

valuepunctuate
  1. :: Doc ann

    Punctuation, e.g. comma

  2. -> [Doc ann]
  3. -> [Doc ann]
#

(punctuate p xs) appends p to all but the last document in xs.

Example2 expressions
let docs = punctuate comma (Util.words "lorem ipsum dolor sit amet")putDocW 80 (hsep docs)lorem, ipsum, dolor, sit, amet

The separators are put at the end of the entries, which we can see if we position the result vertically:

Example1 expression
putDocW 20 (vsep docs)lorem,ipsum,dolor,sit,amet

If you want put the commas in front of their elements instead of at the end, you should use tupled or, in general, encloseSep.

Reactive/conditional layouts

4 declarations

Lay documents out differently based on current position and the page layout.

valuecolumn :: (Int -> Doc ann) -> Doc ann
#

Layout a document depending on which column it starts at. align is implemented in terms of column.

Example1 expression
column (\l -> "Columns are" <+> pretty l <> "-based.")Columns are 0-based.
Example2 expressions
let doc = "prefix" <+> column (\l -> "| <- column" <+> pretty l)vsep [indent n doc | n <- [0,4,8]]prefix | <- column 7    prefix | <- column 11        prefix | <- column 15
valuenesting :: (Int -> Doc ann) -> Doc ann
#

Layout a document depending on the current nesting level. align is implemented in terms of nesting.

Example2 expressions
let doc = "prefix" <+> nesting (\l -> brackets ("Nested:" <+> pretty l))vsep [indent n doc | n <- [0,4,8]]prefix [Nested: 0]    prefix [Nested: 4]        prefix [Nested: 8]
valuewidth :: Doc ann -> (Int -> Doc ann) -> Doc ann
#

(width doc f) lays out the document doc, and makes the column width of it available to a function.

Example2 expressions
let annotate doc = width (brackets doc) (\w -> " <- width:" <+> pretty w)align (vsep (map annotate ["---", "------", indent 3 "---", vsep ["---", indent 4 "---"]]))[---] <- width: 5[------] <- width: 8[   ---] <- width: 8[---    ---] <- width: 8
valuepageWidth :: (PageWidth -> Doc ann) -> Doc ann
#

Layout a document depending on the page width, if one has been specified.

Example3 expressions
let prettyPageWidth (AvailablePerLine l r) = "Width:" <+> pretty l <> ", ribbon fraction:" <+> pretty rlet doc = "prefix" <+> pageWidth (brackets . prettyPageWidth)putDocW 32 (vsep [indent n doc | n <- [0,4,8]])prefix [Width: 32, ribbon fraction: 1.0]    prefix [Width: 32, ribbon fraction: 1.0]        prefix [Width: 32, ribbon fraction: 1.0]

Filler functions

2 declarations

Fill up available space

valuefill
  1. :: Int

    Append spaces until the document is at least this wide

  2. -> Doc ann
  3. -> Doc ann
#

(fill i x) lays out the document x. It then appends spaces until the width is equal to i. If the width of x is already larger, nothing is appended.

This function is quite useful in practice to output a list of bindings:

Example3 expressions
let types = [("empty","Doc"), ("nest","Int -> Doc -> Doc"), ("fillSep","[Doc] -> Doc")]let ptype (name, tp) = fill 5 (pretty name) <+> "::" <+> pretty tp"let" <+> align (vcat (map ptype types))let empty :: Doc    nest  :: Int -> Doc -> Doc    fillSep :: [Doc] -> Doc
valuefillBreak
  1. :: Int

    Append spaces until the document is at least this wide

  2. -> Doc ann
  3. -> Doc ann
#

(fillBreak i x) first lays out the document x. It then appends spaces until the width is equal to i. If the width of x is already larger than i, the nesting level is increased by i and a line is appended. When we redefine ptype in the example given in fill to use fillBreak, we get a useful variation of the output:

Example3 expressions
let types = [("empty","Doc"), ("nest","Int -> Doc -> Doc"), ("fillSep","[Doc] -> Doc")]let ptype (name, tp) = fillBreak 5 (pretty name) <+> "::" <+> pretty tp"let" <+> align (vcat (map ptype types))let empty :: Doc    nest  :: Int -> Doc -> Doc    fillSep          :: [Doc] -> Doc

General convenience

3 declarations

Useful helper functions.

valueplural
  1. :: (Num amount, Eq amount)
  2. => doc

    1 case

  3. -> doc

    other cases

  4. -> amount
  5. -> doc
#

(plural n one many) is one if n is 1, and many otherwise. A typical use case is adding a plural "s".

Example3 expressions
let things = [True]let amount = length thingspretty things <+> "has" <+> pretty amount <+> plural "entry" "entries" amount[True] has 1 entry
valueenclose
  1. :: Doc ann

    L

  2. -> Doc ann

    R

  3. -> Doc ann

    x

  4. -> Doc ann

    LxR

#

(enclose l r x) encloses document x between documents l and r using <>.

Example1 expression
enclose "A" "Z" "·"A·Z
enclose l r x = l <> x <> r
valuesurround :: Doc ann -> Doc ann -> Doc ann -> Doc ann
#

(surround x l r) surrounds document x with l and r.

Example1 expression
surround "·" "A" "Z"A·Z

This is merely an argument reordering of enclose, but allows for definitions like

Example1 expression
concatWith (surround dot) ["Prettyprinter", "Render", "Text"]Prettyprinter.Render.Text

Bracketing functions

6 declarations

Enclose documents in common ways.

valuesquotes :: Doc ann -> Doc ann
#
Example1 expression
squotes "·"'·'
valuedquotes :: Doc ann -> Doc ann
#
Example1 expression
dquotes "·""·"
valueparens :: Doc ann -> Doc ann
#
Example1 expression
parens "·"(·)
valueangles :: Doc ann -> Doc ann
#
Example1 expression
angles "·"<·>
valuebrackets :: Doc ann -> Doc ann
#
Example1 expression
brackets "·"[·]
valuebraces :: Doc ann -> Doc ann
#
Example1 expression
braces "·"{·}

Named characters

19 declarations

Convenience definitions for common characters

valuelparen :: Doc ann
#
Example1 expression
lparen(
valuerparen :: Doc ann
#
Example1 expression
rparen)
valuelangle :: Doc ann
#
Example1 expression
langle<
valuerangle :: Doc ann
#
Example1 expression
rangle>
valuelbrace :: Doc ann
#
Example1 expression
lbrace{
valuerbrace :: Doc ann
#
Example1 expression
rbrace}
valuelbracket :: Doc ann
#
Example1 expression
lbracket[
valuerbracket :: Doc ann
#
Example1 expression
rbracket]
valuesquote :: Doc ann
#
Example1 expression
squote'
valuedquote :: Doc ann
#
Example1 expression
dquote"
valuesemi :: Doc ann
#
Example1 expression
semi;
valuecolon :: Doc ann
#
Example1 expression
colon:
valuecomma :: Doc ann
#
Example1 expression
comma,
valuespace :: Doc ann
#
Example1 expression
"a" <> space <> "b"a b

This is mostly used via <+>,

Example1 expression
"a" <+> "b"a b
valuedot :: Doc ann
#
Example1 expression
dot.
valueslash :: Doc ann
#
Example1 expression
slash/
valueequals :: Doc ann
#
Example1 expression
equals=
valuepipe :: Doc ann
#
Example1 expression
pipe|

Annotations

valueannotate :: ann -> Doc ann -> Doc ann
#

Add an annotation to a Doc. This annotation can then be used by the renderer to e.g. add color to certain parts of the output. For a full tutorial example on how to use it, see the Prettyprinter.Render.Tutorials.StackMachineTutorial or Prettyprinter.Render.Tutorials.TreeRenderingTutorial modules.

This function is only relevant for custom formats with their own annotations, and not relevant for basic prettyprinting. The predefined renderers, e.g. Prettyprinter.Render.Text, should be enough for the most common needs.

valueunAnnotate :: Doc ann -> Doc xxx
#

Remove all annotations.

Although unAnnotate is idempotent with respect to rendering,

unAnnotate . unAnnotate = unAnnotate

it should not be used without caution, for each invocation traverses the entire contained document. If possible, it is preferrable to unannotate after producing the layout by using unAnnotateS.

valuereAnnotate :: (ann -> ann') -> Doc ann -> Doc ann'
#

Change the annotation of a Document.

Useful in particular to embed documents with one form of annotation in a more generally annotated document.

Since this traverses the entire Doc tree, including parts that are not rendered due to other layouts fitting better, it is preferrable to reannotate after producing the layout by using reAnnotateS.

Since reAnnotate has the right type and satisfies 'reAnnotate id = id', it is used to define the Functor instance of Doc.

valuealterAnnotations :: (ann -> [ann']) -> Doc ann -> Doc ann'
#

Change the annotations of a Document. Individual annotations can be removed, changed, or replaced by multiple ones.

This is a general function that combines unAnnotate and reAnnotate, and it is useful for mapping semantic annotations (such as »this is a keyword«) to display annotations (such as »this is red and underlined«), because some backends may not care about certain annotations, while others may.

Annotations earlier in the new list will be applied earlier, i.e. returning [Bold, Green] will result in a bold document that contains green text, and not vice-versa.

Since this traverses the entire Doc tree, including parts that are not rendered due to other layouts fitting better, it is preferrable to reannotate after producing the layout by using alterAnnotationsS.

Optimization

2 declarations
valuefuse :: FusionDepth -> Doc ann -> Doc ann
#

(fuse depth doc) combines text nodes so they can be rendered more efficiently. A fused document is always laid out identical to its unfused version.

When laying a Document out to a SimpleDocStream, every component of the input is translated directly to the simpler output format. This sometimes yields undesirable chunking when many pieces have been concatenated together.

For example

Example1 expression
"a" <> "b" <> pretty 'c' <> "d"abcd

results in a chain of four entries in a SimpleDocStream, although this is fully equivalent to the tightly packed

Example1 expression
"abcd" :: Doc annabcd

which is only a single SimpleDocStream entry, and can be processed faster.

It is therefore a good idea to run fuse on concatenations of lots of small strings that are used many times:

Example2 expressions
let oftenUsed = fuse Shallow ("a" <> "b" <> pretty 'c' <> "d")hsep (replicate 5 oftenUsed)abcd abcd abcd abcd abcd
datadata FusionDepth
#

Fusion depth parameter, used by fuse.

Constructors

  • Shallow

    Do not dive deep into nested documents, fusing mostly concatenations of text nodes together.

  • Deep

    Recurse into all parts of the Doc, including different layout alternatives, and location-sensitive values such as created by nesting which cannot be fused before, but only during, the layout process. As a result, the performance cost of using deep fusion is often hard to predict, and depends on the interplay between page layout and document to prettyprint.

    This value should only be used if profiling shows it is significantly faster than using Shallow.

Instances3Eq, Ord, Show
  • Eq FusionDepthDefined in prettyprinter-1.7.1 · Prettyprinter.Internal
  • Ord FusionDepthDefined in prettyprinter-1.7.1 · Prettyprinter.Internal
  • Show FusionDepthDefined in prettyprinter-1.7.1 · Prettyprinter.Internal

Layout

8 declarations

Laying a Document out produces a straightforward SimpleDocStream based on parameters such as page width and ribbon size, by evaluating how a Doc fits these constraints the best. There are various ways to render a SimpleDocStream. For the common case of rendering a SimpleDocStream as plain Text take a look at Prettyprinter.Render.Text.

datadata SimpleDocStream ann
#

The data type SimpleDocStream represents laid out documents and is used by the display functions.

A simplified view is that Doc = [SimpleDocStream], and the layout functions pick one of the SimpleDocStreams based on which one fits the layout constraints best. This means that SimpleDocStream has all complexity contained in Doc resolved, making it very easy to convert it to other formats, such as plain text or terminal output.

To write your own Doc to X converter, it is therefore sufficient to convert from SimpleDocStream. The »Render« submodules provide some built-in converters to do so, and helpers to create own ones.

Constructors

Instances8Functor, Foldable, Traversable, Eq, Ord, Show, …
datadata PageWidth
#

Maximum number of characters that fit in one line. The layout algorithms will try not to exceed the set limit by inserting line breaks when applicable (e.g. via softline').

Constructors

  • AvailablePerLine !Int !Double

    Layouters should not exceed the specified space per line.

    • The Int is the number of characters, including whitespace, that fit in a line. A typical value is 80.

    • The Double is the ribbon with, i.e. the fraction of the total page width that can be printed on. This allows limiting the length of printable text per line. Values must be between 0 and 1, and 0.4 to 1 is typical.

  • Unbounded

    Layouters should not introduce line breaks on their own.

Instances3Eq, Ord, Show
  • Eq PageWidthDefined in prettyprinter-1.7.1 · Prettyprinter.Internal
  • Ord PageWidthDefined in prettyprinter-1.7.1 · Prettyprinter.Internal
  • Show PageWidthDefined in prettyprinter-1.7.1 · Prettyprinter.Internal

The default layout options, suitable when you just want some output, and don’t particularly care about the details. Used by the Show instance, for example.

Example1 expression
defaultLayoutOptionsLayoutOptions {layoutPageWidth = AvailablePerLine 80 1.0}
valuelayoutPretty :: LayoutOptions -> Doc ann -> SimpleDocStream ann
#

This is the default layout algorithm, and it is used by show, putDoc and hPutDoc.

layoutPretty commits to rendering something in a certain way if the next element fits the layout constraints; in other words, it has one SimpleDocStream element lookahead when rendering. Consider using the smarter, but a bit less performant, layoutSmart algorithm if the results seem to run off to the right before having lots of line breaks.

valuelayoutCompact :: Doc ann1 -> SimpleDocStream ann2
#

(layoutCompact x) lays out the document x without adding any indentation and without preserving annotations. Since no 'pretty' printing is involved, this layouter is very fast. The resulting output contains fewer characters than a prettyprinted version and can be used for output that is read by other programs.

Example2 expressions
let doc = hang 4 (vsep ["lorem", "ipsum", hang 4 (vsep ["dolor", "sit"])])doclorem    ipsum    dolor        sit
Example2 expressions
let putDocCompact = renderIO System.IO.stdout . layoutCompactputDocCompact docloremipsumdolorsit
valuelayoutSmart :: LayoutOptions -> Doc ann -> SimpleDocStream ann
#

A layout algorithm with more lookahead than layoutPretty, that introduces line breaks earlier if the content does not (or will not, rather) fit into one line.

Consider the following python-ish document,

Example2 expressions
let fun x = hang 2 ("fun(" <> softline' <> x) <> ")"let doc = (fun . fun . fun . fun . fun) (align (list ["abcdef", "ghijklm"]))

which we’ll be rendering using the following pipeline (where the layout algorithm has been left open):

Example4 expressions
import Data.Text.IO as Timport Prettyprinter.Render.Textlet hr = pipe <> pretty (replicate (26-2) '-') <> pipelet go layouter x = (T.putStrLn . renderStrict . layouter (LayoutOptions (AvailablePerLine 26 1))) (vsep [hr, x, hr])

If we render this using layoutPretty with a page width of 26 characters per line, all the fun calls fit into the first line so they will be put there:

Example1 expression
go layoutPretty doc|------------------------|fun(fun(fun(fun(fun(                  [ abcdef                  , ghijklm ])))))|------------------------|

Note that this exceeds the desired 26 character page width. The same document, rendered with layoutSmart, fits the layout contstraints:

Example1 expression
go layoutSmart doc|------------------------|fun(  fun(    fun(      fun(        fun(          [ abcdef          , ghijklm ])))))|------------------------|

The key difference between layoutPretty and layoutSmart is that the latter will check the potential document until it encounters a line with the same indentation or less than the start of the document. Any line encountered earlier is assumed to belong to the same syntactic structure. layoutPretty checks only the first line.

Consider for example the question of whether the As fit into the document below:

1 A
2   A
3  A
4 B
5   B

layoutPretty will check only line 1, ignoring whether e.g. line 2 might already be too wide. By contrast, layoutSmart stops only once it reaches line 4, where the B has the same indentation as the first A.

Remove all trailing space characters.

This has some performance impact, because it does an entire additional pass over the SimpleDocStream.

No trimming will be done inside annotations, which are considered to contain no (trimmable) whitespace, since the annotation might actually be about the whitespace, for example a renderer that colors the background of trailing whitespace, as e.g. git diff can be configured to do.

Historical note: Since v1.7.0, layoutPretty and layoutSmart avoid producing the trailing whitespace that was the original motivation for creating removeTrailingWhitespace. See https://github.com/quchen/prettyprinter/pull/139 for some background info.

Migration guide

0 declarations

There are 3 main ways to migrate:

  1. Direct: just replace the previous package and fix the errors

  2. Using a drop-in replacement mimicing the API of the former module, see the prettyprinter-compat-package packages

  3. Using a converter from the old Doc type to the new one, see the prettyprinter-convert-package packages

If you're already familiar with (ansi-)wl-pprint, you'll recognize many functions in this module, and they work just the same way. However, a couple of definitions are missing:

  • char, string, double, … – these are all special cases of the overloaded pretty function.

  • <$>, <$$>, </>, <//> are special cases of vsep, vcat, fillSep, fillCat with only two documents.

  • If you need String output, use the backends in the Prettyprinter.Render.String module.

  • The display functions are moved to the rendering submodules, for example conversion to plain Text is in the Prettyprinter.Render.Text module.

  • The render functions are called layout functions.

  • SimpleDoc was renamed to SimpleDocStream, in order to make it clearer in the presence of SimpleDocTree.

  • Instead of providing an own colorization function for each color/intensity/layer combination, they have been combined in color, colorDull, bgColor, and bgColorDull functions, which can be found in the ANSI terminal specific prettyprinter-ansi-terminal package.