Hey Laurent,

Making this more comprehensible is part of my plan for documenting how
to create dispatch. You don't really need to understand the whole XP
paper to know enough to understand how pretty print works. Let me lay
out a quick overview here:

High-level concepts

Top-level concept: the pretty printer tries to keep things together on
a line unless they don't fit, in which case it splits the lines in
appropriate places and indents successive lines to the right place.

2nd top-level concept: pretty printing is a mechanism, not a fixed
function. The actual printing is controlled by dispatch functions that
in turn call functions in the underlying mechanism. There are four
examples you can look at to see how this works in Clojure: two
functions in clojure/contrib/pprint/dispatch.clj that implement the
standard "simple" dispatch and the specialized "code" dispatch. The
other two are in clojure/contrib/pprint/examples and format clojure
structure as pretty printed json or xml.

There are two ways to define the dispatch function (which is basically
always a multimethod because you do different things for different
types): writing it out as a function or specifying a format for cl-
format using the formatter-out function. I'll assume the first method
here, since it will be more familiar.

Logical blocks

The "things" that the pretty printer thinks about are "logical
blocks." These usually correspond to things like lists, vectors, maps
and such. Logical blocks are nested (just like lists, vectors,...).

Logical blocks are created by the macro pprint-logical-block. This
macro wraps its body in a logical block and allows you to specify
prefix and suffix (e.g., "(" and ")").

Control variables

There are two key variables that control where the pretty printer
decides to break lines, *print-right-margin* and *print-miser-width*.
The first is the most significant and tells pprint where you want it
to try get the line to break. *print-miser-width* is a width back to
the left from the right margin where you want pprint to be really
aggressive about breaking lines (this is useful for heavily nested
structures).

For example, "let" may not break between the let and the binding
vector, but if the let construct is to the right of the miser width
boundary (i.e. very indented), it might put a newline there. This
makes it more likely that we'll be able to fit even more deeply nested
structures inside the right margin.

Note that the pretty printer doesn't completely guarantee that it will
be able to print a structure within the right margin, but it does a
pretty good job in practice.

Newlines

The are 4 kinds of newlines that the dispatch function can specify:
linear, fill, miser, and mandatory. These are created by the pprint-
newline function which takes a keyword argument specifying the type.
Simplifying a little, we can consider each type:

Linear: If a logical block doesn't fit on a single line, *all* the
linear newlines in it are emitted as real newlines, otherwise none of
them are. (For example, simple-dispatch specifies a linear newline
between each element of a list. So either the list is printed on a
single line, or every element is printed on its own line).

Fill: If a logical block doesn't fit on a single line, only those fill
newlines that are necessary to make the parts fit are emitted and the
rest are ignored. (For example, when formatting XML attributes, we fit
as many as possible on a single line before inserting a newline.)

Miser: If you see a miser newline when you are to the right of the
miser width boundary, miser newlines are emitted, otherwise they are
ignored.

Mandatory: A mandatory newline is always emitted.

Indenting

By default, when a newline is emitted the next line is indented to the
column where the first character of the current logical block was
emitted (after any prefix). So simple list structures line up right
after the opening paren, for instance.

You can override this default with pprint-indent which allows you to
specify an indent relative to the beginning of the block or the
current column position.

Write and write-out

"write" is a general function which will (in general) allow you to
emit an object using the pretty printer. In dispatch functions, we use
"write-out" which is a version of write that assumes that *out* has
been correctly set up for the pretty printer. You can think of write-
out as a recursive call to the pretty printer when you see it or use
it in dispatch functions.

I think that should give you enough to get started with. Check out the
dispatch functions I mentioned above to see all of this in action. And
let me know if I clarify any more stuff.

Tom


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to