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.