Forum: Public ReviewIssue: GENERALIZE-PRETTY-PRINTER
References: Loosemore's public review comment #18 (X3J13/92-1318)
Section 22.2.1 (Pretty Printer Concepts), p 22-13
~/name/ FORMAT directive, p 22-39..22-40
PPRINT-DISPATCH, p 22-45..22-46
PPRINT-EXIT-IF-LIST-EXHAUSTED, p 22-46
PPRINT-FILL (etc), p 22-47
PPRINT-INDENT, p 22-48
PPRINT-LOGICAL-BLOCK, p 22-49..22-51
PPRINT-NEWLINE, p 22-51..22-52
PPRINT-POP, p 22-52..22-54
PPRINT-TAB, p 22-54
PRINT-OBJECT, p 22-55..22-56
SET-PPRINT-DISPATCH, p 22-57..22-58
Category: CHANGE
Edit history: V1, 18 Sept 1992, Sandra Loosemore
V2, 18 Sept 1992, Sandra Loosemore
(comments from Dick Waters)
V3, 04 Feb 1993, Sandra Loosemore
(fix references)
Status: Proposal UNIFY passed 8-3 on letter ballot 93-302.
Problem description:
The pretty-printing mechanisms are poorly integrated with the rest
of the printer.
Specifically,
(1) The requirement that objects for which there is no pretty-print
function defined in *PRINT-PPRINT-DISPATCH* print using the standard
mechanisms but with *PRINT-PRETTY* bound to NIL (p. 22-15 and 22-45)
makes it impossible for PRINT-OBJECT methods or DEFSTRUCT
:PRINT-FUNCTIONS to handle *PRINT-PRETTY* themselves. This can lead
to unnecessary duplication of code, as it requires programmers to write
two different functions to print the same kind of object.
(2) The behavior of PPRINT-FILL, PPRINT-LINEAR, PPRINT-TABULAR,
PPRINT-INDENT, PPRINT-LOGICAL-BLOCK, PPRINT-NEWLINE, and PPRINT-TAB when
*PRINT-PRETTY* is not true is unspecified, although the names imply
that they are only useful when pretty-printing or that they may bind
*PRINT-PRETTY*. Again, to avoid pointless duplication of code, these
mechanisms should do something reasonable regardless of whether
*PRINT-PRETTY* is true.
(3) The order of the "stream" and "object" arguments to
PPRINT-FILL, PPRINT-LINEAR, PPRINT-TABULAR, functions in pprint
dispatch tables, and functions used with the ~/name/ format directive
is backwards with respect to that of other printing functions.
There are three non-mutually-exclusive proposals here. Proposal
UNIFY addresses problems (1) and (2). Proposal REORDER addresses
problem (3). Proposal RENAME changes the names of most of the
affected functions to reflect the more general utility under proposal
UNIFY.
Proposal (GENERALIZE-PRETTY-PRINTER:UNIFY):
(1) Change the behavior stated in section 22.2.1.4 and in the
description of PPRINT-DISPATCH to say that if there is no specific
found in the pprint dispatch table, a function which invokes the
appropriate PRINT-OBJECT method is used. Remove the requirement that
*PRINT-PRETTY* be rebound to NIL.
(2) Clarify that PPRINT-LOGICAL-BLOCK may be used even when
*PRINT-PRETTY* is not true. Remove the requirement that the stream
cease to behave as a pretty-printing stream if *PRINT-PRETTY* becomes
bound to NIL. Clarify that PPRINT-LOGICAL-BLOCK does not locally
rebind *PRINT-PRETTY*.
(3) Specify that if *PRINT-PRETTY* is false, the functions PPRINT-FILL,
PPRINT-LINEAR, and PPRINT-TABULAR print list arguments with a minimum
of whitespace (i.e., as described in section 22.1.3.8). Clarify that
these functions do not locally rebind *PRINT-PRETTY*.
(4) The draft currently says that PPRINT-NEWLINE, PPRINT-TAB, and
PPRINT-INDENT do nothing if the stream is not a pretty-printing stream.
Change this to say they also do nothing if *PRINT-PRETTY* is false.
(5) Change the description of PRINT-OBJECT to mention the use of
functions such as PPRINT-FILL to handle *PRINT-PRETTY*, and
PPRINT-LOGICAL-BLOCK to handle *PRINT-LENGTH* truncation, instead
of discouraging people from handling these variables properly.
Rationale:
This allows you to write a single function that handles both pretty
and non-pretty printing that can use a single block of code inside
PPRINT-LOGICAL-BLOCK to print structured objects regardless of whether
*PRINT-PRETTY* is true. It also allows you to call the other
functions without having to make an explicit check to be sure that
*PRINT-PRETTY* is true, and do something else if it isn't.
Proposal (GENERALIZE-PRETTY-PRINTER:REORDER):
(1) Reverse the order of the "object" and "stream" arguments to
functions in pprint dispatch tables.
(2) Explicitly state that PPRINT-DISPATCH returns the generic function
PRINT-OBJECT if there is no matching type specifier in the pprint
dispatch table.
(3) Reverse the order of the "object" and "stream" arguments to
PPRINT-FILL, PPRINT-LINEAR, and PPRINT-TABULAR. Make the "stream"
argument optional and default in the usual way for an output stream
designator, so that the argument list is
(object &optional stream colon-p at-sign-p).
(4) Reverse the order of the "object" and "stream" arguments passed
to functions invoked with the ~/name/ format directive.
Rationale:
This removes a pointless inconsistency in the language design and
makes it a little simpler.
Proposal (GENERALIZE-PRETTY-PRINTER:RENAME):
Rename PPRINT-EXIT-IF-LIST-EXHAUSTED, PPRINT-FILL, PPRINT-LINEAR,
PPRINT-TABULAR, PPRINT-INDENT, PPRINT-LOGICAL-BLOCK, PPRINT-NEWLINE,
PPRINT-POP, and PPRINT-TAB to PRINT-*.
Rationale:
Changing the names makes it more apparent that these functions are
useful even when *PRINT-PRETTY* is false and that they do not
rebind *PRINT-PRETTY* to true as PPRINT does. (Note that this list
does not include *PRINT-PPRINT-DISPATCH*, PPRINT-DISPATCH, and
SET-PPRINT-DISPATCH, since these are only relevant when *PRINT-PRETTY*
is true.)
Since proposals UNIFY and REORDER make incompatible changes in the
behavior of these functions, changing their names may be helpful in
detecting obsolete calls.
Current practice:
XP doesn't implement any of these changes.
Loosemore made most of these changes when rewriting XP into a
Scheme-like dialect; the Yale Haskell system uses it extensively.
Cost to implementors:
Medium. Making the changes to the printer implementation is
straightforward, but there are the usual support costs associated
with making an incompatible change.
Cost to users:
This is an incompatible change for people who now use XP, but it's
easy to fix. Adoption of proposal RENAME may make it easier to detect
obsolete uses.
Cost of non-adoption:
More work for people writing new code. Confusion about why
PRINT-OBJECT methods can't detect when *PRINT-PRETTY* is true. Risk
of function for printing when *PRINT-PRETTY* is true getting out of
sync with function for printing the same object when *PRINT-PRETTY* is
false. Risk of people making stupid programming mistakes because they
can't remember which functions accept arguments in which order.
Benefits:
The cost of non-adoption is avoided.
Aesthetics:
Making the language simpler and more uniform is generally a good
thing.
Editorial impact:
It would probably take two or three days to make the necessary changes,
including updating examples. Loosemore is willing to undertake the
editorial work.
Discussion:
This issue was first raised on the X3J13 mailing list in January 1992
with the subject "apparent shortcomings with PPRINT interface".
Loosemore says: I discovered these problems when trying to implement
the new pretty-printer functionality for the first time for use by the
Yale Haskell system. We have several dozen different kinds of structures
representing AST nodes and wanted to be able to print them reasonably
for debugging and error messages. Pushing the detection for
*PRINT-PRETTY* into the support functions resulted in *major*
consolidations and simplifications of this code.
Waters says: I support your proposal strongly. Please feel free to
add me to the proposal as a formal supporter.
John Burger also says he was bitten by the inability to handle
*PRINT-PRETTY* in PRINT-OBJECT methods, and that he supports this
proposal.