## Pretty Printing

The way pretty printing is handled in Poly/ML changed in version 5.3. This describes the functions and datatypes that were added in that version. The old mechanism, using `PolyML.install_pp`, is deprecated and may be removed in a future release.

The basic model, described in the paper by D.C. Oppen ACM ToPLAS Vol. 2 No. 4 Oct 1980, is unchanged but instead of a set of imperative functions pretty printing is structured around the `PolyML.pretty` datatype and the pretty printing algorithm is now functional rather than imperative.

    datatype pretty =
         PrettyBlock of int * bool * context list * pretty list
       | PrettyBreak of int * int
       | PrettyString of string

    and context =
        ContextLocation of location
      | ContextParentStructure of string * context list
      | ContextProperty of string * string

    withtype location =
       { file: string, startLine: int, startPosition: int, endLine: int, endPosition: int }

`PrettyString s` contains a string to be printed. `PrettyBlock(indent, consistent, context, items)` defines a block of `items` which may be strings, breaks or blocks. The items will be retained on a single line if possible. The `indent` parameter is an indentation that will be added to the current indentation if the block has to be broken. Note that this does not apply to the first item in the block which will always be printed with the current indentation. The `consistent` parameter indicates whether the block is to be broken consistently (true) or not (false). If it is true then if the block will not all fit on a line and must be broken then it will be broken at all the breaks in the block whether this is necessary or not. If it is false it will only be broken where necessary. Neither of these parameters are used if the block will fit on a line. `PrettyBreak(blanks, breakOffset)` indicates a break between items. If the line is not broken at this point then `blanks` is the number of space characters that will be inserted. If the line is broken at that point then instead the following item is indented by an extra `breakOffset` spaces.

The context and location types are primarily used by the IDE when providing error messages. For most purposes the `context` argument to `PrettyBlock` can be the empty list. `ContextProperty` can be used by a user-supplied pretty printer to provide extra information which may be useful if the result of pretty printing is to be processed by a user function. It is not produced by Poly/ML pretty printers and the default printers ignore this item.`ContextParentStructure` was used to deal with types inside structures in an early draft and will probably be removed.

A pretty printer can be associated with a datatype using `PolyML.addPrettyPrinter`.

    val addPrettyPrinter: (int -> 'a -> 'b -> pretty)-> unit

This function has a polymorphic type but is specially handled by the compiler. `addPrettyPrinter pp` installs a pretty printer `pp` where `pp` has arguments `depth printArgTypes value`. The first argument, `depth`, is the print depth. This is a value that indicates how much of the data structure should be displayed. If this value is zero or negative the pretty printer should always print a simple string such as `PrettyString "..."`. The third argument, `value`, is a value of the datatype. When installing a pretty printer there must be sufficient type constraint so that the compiler is able to determine the type unambiguiously. The second argument, `printArgTypes`, is only used for polytypes i.e. datatypes defined as `'a t` or `('a, 'b', 'c ...) t`. It is not used for monotypes. If the type takes a single argument then `printArgTypes` has type `'a * int -> pretty` and is the function that will generate the pretty data structure for the argument type. The `int` argument is the adjusted print depth and should normally be one less than the value of `depth`. If the type takes multiple arguments then `printArgTypes` is a tuple with each field being a function of type `'a * int -> pretty` that is used for the corresponding argument of the datatype.

As well as PolyML.addPrettyPrinter there some other functions in the PolyML structure that may be useful.

    val prettyRepresentation : 'a * int -> pretty

This function returns the `pretty` structure that can be used to print a value of the given type up to the specified depth. It is similar to `PolyML.print` in being infinitely overloaded. It can be useful when writing a pretty printer for a datatype that includes types that already have pretty printers installed or where they will be installed later since it uses any pretty printers for the types when it is actually called.

    val prettyPrint: (string -> unit) * int -> pretty -> unit

This function takes an output function and a line length and outputs a pretty structure, interpretting the layout information.