Re: Problem with SBCL pprint

Richard M Kreuter via Sbcl-help <[email protected]> Wed, 08 Apr 2026 17:24:08 -0400
Newsgroups gmane.lisp.steel-bank.general
Message-ID <[email protected]>
Douglas Katzman <[email protected]> wrote:

> [ANSI] says that ~D,~B,~O,~X,~R (and others) rebind printer
> controls... [W]hat's the point of specifying mandatory rebindings if
> not to either permit or require user-written functions to take over
> the printing as per the print-pretty mechanism?

~D,~B,~O,~X,~nR print a non-integer argument "as by PRINC". So the
mandatory rebindings have an effect when the argument isn't an
integer. What if that was the only point?

In fact, the rules about variable rebinding were finalized months before
Waters's pretty printer was proposed (note the draft dates):

https://www.lispworks.com/documentation/HyperSpec/Issues/iss169_w.htm
https://www.lispworks.com/documentation/HyperSpec/Issues/iss270_w.htm

And before Waters's pretty printer, the variable binding rules could not
have had an observable effect on output from conforming programs' uses
of ~D,~O,~B,~X,~R when the argument is an integer.

So the original point of variable binding rules could not have had to do
with pretty printing. I don't think Waters's pretty printer compelled a
reinterpretation of the role of those rules, as I'll explain below.

(Candidly, I can't offer a rationale for every binding
FORMAT-PRETTY-PRINT required. Some seem unnecessary or redundant, and
the Cleanup committee thread looks like bikeshedding.)

Douglas Katzman <[email protected]> wrote:

> Based on some discussion here, I think we would be receptive to
> well-reasoned claims that ~D,~O,~B,~X "definitely" meant that a value
> is output exactly as specified in the writeup for that directive
> regardless of *print-pretty*, provided the value is an integer. I
> think the claim would be along the line that use of PRINC was merely a
> hidden detail of the implementation in some cases, not a mandate to
> use it, and that CLHS allows either interpretation of whether to
> respect *print-pretty* except in the case when PRINC _must_ be called
> due to the fallback to "~A" when the argument is not an
> integer. Something like that.

I'll stipulate I can't find anything in CLHS to disambiguate things.
However, I don't think the implemented behavior was intended or the
Right Thing for any directives that aren't required to invoke the
printer (~W, ~S, ~A, and ones that fall through to ~A).

I think intent is discoverable enough: Waters's XP, which was presented
as a reference implementation of the pretty printer interface, only
invoked a pretty printer function for ~W, ~S, ~A. This code is neither
bug-free nor normative, but there's a long comment suggesting that his
handling of ~C,~D,~O,~B,~X,~R,~F,~E,~G,~$ was deliberate. So ISTM likely
that the reason his spec was silent about those directives is because he
intended their behavior to remain constant.

https://www.cs.cmu.edu/afs/cs.cmu.edu/project/ai-repository/ai/lang/lisp/code/io/xp/

What's more important is how/why that behavior could make sense.

The canonical use of the pretty printing interface is to construct
variants of the "vanilla" Lisp printer that produce extra whitespace &
perhaps style certain things interestingly. The Lisp printer is
parameterized by printer controls, and the entirety of a printed
representation customarily presents all objects as if under a constant
complement of printer control settings. FORMAT directives that present
an atom force styling that disagrees with the Lisp printer: each of
~C,~D,~O,~B,~X,~R,~A,~S, overrule some printer control variable, and
each of ~F,~E,~G,~$ is able to disagree with PRIN1 for some float. So
canonical pretty printer applications won't use these directives very
much. (I believe SBCL's pretty printer has just one use: ~D for an
array's rank. More on that below.)

If canonical uses won't involve ~C,~D,~O,~B,~X,~R,~F,~E,~G,~$, why have
them invoke pretty printer functions? And if there are other,
non-canonical applications (like Robert's) that could use
~C,~D,~O,~B,~X,~R,~F,~E,~G,~$ for their standard styling effects, isn't
it more convenient for the user not to have to worry about regress? That
is, making ~C,~D,~O,~B,~X,~R,~F,~E,~G,~$ "base cases" seems to be a pure
win over the status quo.

Moreover, making ~C,~D,~O,~B,~X,~R,~F,~E,~G,~$ base cases would
eliminate some head-scratchers for users of SBCL. One head-scratcher is
how to decide who's at fault here and what to do about it:

  * (let ((*print-pretty* t)
        (*print-pprint-dispatch* (copy-pprint-dispatch nil)))
      (set-pprint-dispatch
       'integer (lambda (s i) i (write-string "AAA" s)))
      (time (sleep 1)))
  Evaluation took:
    AAA.00AAA seconds of real time
    0.000AAA seconds of total run time (0.0000AAA user, 0.000AAA system)
    0.10% CPU
    AAA forms interpreted
    AAA processor cycles
    AAA bytes consed

A subtler head-scratcher is whether the fact that installing a custom
integer printer does /not/ break how the rank appears in the output of
(pprint (make-array '(2 2))) dependends on the head-scratcher that "~D"
only /sometimes/ invokes a pretty printer function.

Finally, and more abstractly, isn't it a more parsimonious
interpretation of the standard to say that a FORMAT directive is to
behave as if it invokes the Lisp printer only when the CLHS actually
says it is to do so?

Regards,
Richard