From: Tony Martínez
Subject: Pretty printing streams and *print-right-margin* modification
Date: 
Message-ID: <b32da960.0212301132.1a158173@posting.google.com>
Hello all,

I'd be most grateful if anyone could help me to understand the
following pprinting behaviour.  Should this form print two lines or
one?

    (let ((*print-right-margin* 1))
      (pprint-logical-block (*standard-output* (list 0))
        (princ 0)
        (let ((*print-right-margin* 10))
          (pprint-logical-block (*standard-output* (list 1))
            (pprint-newline :linear)
            (princ 1))))) 

From a quick perusal of the SBCL code, SBCL and CMUCL seem to cache
the value of *print-right-margin* on pretty stream creation, and reuse
the stream if it's already pretty.  This means that only the outermost
pretty stream gets to control line margins, and the previous form
prints two lines.

I've looked in the Hyperspec, but haven't had any luck finding any
information which helps me justify one behaviour or the other, so any
help with the following questions would be much appreciated:

  - Does the CLHS specify either of the previous behaviours?

  - The example may possibly be pathological; I've reduced it from a
  print-object method which produced output which "looked prettier"
  (ahem!) with a smaller margin.  Is this Something I Shouldn't Be
  Doing?  Or is it reasonable to expect dynamically bound values of
  *print-right-margin* to be respected?

  - In the previous implementations, the toplevel print stream
  respects assignments to *print-right-margin*, yet the
  pprint-logical-block bound streams don't.  Would consistency be good
  in this case?

Thanks in advance for any help,

-- 
'tony

From: Steven M. Haflich
Subject: Re: Pretty printing streams and *print-right-margin* modification
Date: 
Message-ID: <3E10D236.3040300@alum.mit.edu>
Tony Mart�nez wrote:

>     (let ((*print-right-margin* 1))
>       (pprint-logical-block (*standard-output* (list 0))
>         (princ 0)
>         (let ((*print-right-margin* 10))
>           (pprint-logical-block (*standard-output* (list 1))
>             (pprint-newline :linear)
>             (princ 1))))) 

It is hard to imagine how the pretty printer could account for
changes to *print-right-margin* during the extent of an
outermost print operation.  The pretty printer necessarily
works by printing and saving _some_ amount of the output, along
with conditional newlines and indentation changes, until it
exceeds the size of a single line.  Then it emits as much of the
saved output as necessary, executing the conditional newlines as
required.  The process requires buffering up as much as a single
line before it can be output.  The problem with changing
*p-r-m* around some portion of the output generation is that it
is not clear when to interpret the value -- when the text of the
printed representation is first generated, or later, when the
buffered output is actually emitted.  Any behavior would be too
suble and complicated to be useful.

> From a quick perusal of the SBCL code, SBCL and CMUCL seem to cache
> the value of *print-right-margin* on pretty stream creation, and reuse
> the stream if it's already pretty.

If it doesn't reinitialize the margin from *p-r-m* each time the
stream is reused, that seems a simple bug.

> This means that only the outermost
> pretty stream gets to control line margins, and the previous form
> prints two lines.

An implementation can work just fine with only a single pretty stream,
created at the outermost entry to the printer.  Each internal
pprint-logical-block does not need to allocate an additional stream.

A lot of this is described in Richar Water's paper(s) on pretty
printing.  I believe there are references cited in CLtL2.
From: Tony Martínez
Subject: Re: Pretty printing streams and *print-right-margin* modification
Date: 
Message-ID: <b32da960.0212310331.7964cdc0@posting.google.com>
"Steven M. Haflich" <·················@alum.mit.edu> wrote in message news:<················@alum.mit.edu>...

[...]

> >From a quick perusal of the SBCL code, SBCL and CMUCL seem to cache
> >the value of *print-right-margin* on pretty stream creation, and reuse
> >the stream if it's already pretty.
> 
> If it doesn't reinitialize the margin from *p-r-m* each time the
> stream is reused, that seems a simple bug.

I don't understand this bit.  If the only pretty stream reuse would
occur in an inner pprint-logical-block (at least in a conforming
program), then CMUCL/SBCL behaviour seems as reasonable to me as any
WRT changes in *print-right-margin*.  Could you elaborate a little on
why it's a bug?

[...]

I've just started using the pretty printer, since a few Lispers at the
ILC spoke highly of it, and am a bit green.  I'll see if I can lay
hands on Richard Waters's XP AI memo, which is the reference you
mentioned in CLtL2.

Thanks very much for your help!

-- 
'tony
From: Steven M. Haflich
Subject: Re: Pretty printing streams and *print-right-margin* modification
Date: 
Message-ID: <3E11C14E.7070207@alum.mit.edu>
Tony Mart�nez wrote:

> I don't understand this bit.  If the only pretty stream reuse would
> occur in an inner pprint-logical-block (at least in a conforming
> program), then CMUCL/SBCL behaviour seems as reasonable to me as any
> WRT changes in *print-right-margin*.  Could you elaborate a little on
> why it's a bug?

First, the pretty printer is invoked (and an encapsulating stream
created) only once at the outermost entry to the printer when
*print-pretty* is true.  There is no need to create additional
streams when inner pprint-logical-blocks are entered.

A pretty printer encapsulating stream is a fairly big and complex
object, and allocating one has consing and initialization costs.
Therefore, it is a common strategy to treat them as a `resource'.
(This use of `resource' has nothing to do with mechanisms of the
same name used in X and MacOS and Java to track auxilliary files
and data etc. needed by an application.)  This is also typically
done for string output streams created by with-output-to-string,
etc.  This is why macros like w-o-t-s and pp-l-b specify that the
stream that is created has only dynamic extent -- so the user
program cannot for some misguided reason retain it outside the
macro form that creates it.

Since these streams are guaranteed garbage, the implementation's
`resource' mechanism can save it away somewhere and reuse it the
next time another stream of the same type is needed.  This can
in practice save a lot of initialization and consing time.  But
it is important that all _necessary_ reinitialization be performed
on the reused stream object, so that reuse functions identically
with a fresh allocation.

That's why the behavior you initially described seemed a simple
bug.  What you described was some sort of resource mechanism,
but it was failing to reinitialize the output width to the
current value of *pp-r-m* when an encapsulation stream is reused.

This should be _very_ easy to fix, once you understand what is
going on.  It might be a 1-liner...

> I've just started using the pretty printer, since a few Lispers at the
> ILC spoke highly of it, and am a bit green.  I'll see if I can lay
> hands on Richard Waters's XP AI memo, which is the reference you
> mentioned in CLtL2.

Waters' reference implementation is also available online, but it
is overkill to spend time studying it.

The pretty printer is indeed a very powerful and useful tool.  In
addition to printing lisp, I use it in my XML/XHTML generator so
that a developer can pprint generated XML.  Pretty printing
consumes additional time and increases the size of output, but
makes complex output readable and debuggable.  Pretty printing
can be turned off after development is complete.  I also use
the pretty printer for generating Java UI code from Lisp, and
it's pretty easy to generate idiomatically indented Java code.
In this case, line breaks are important because java compilers
like to use line numbers in compile-time error messages. :-)
From: ······@terra.es
Subject: Re: Pretty printing streams and *print-right-margin* modification
Date: 
Message-ID: <v49bs2z3n1f.fsf@ece.ericsson.se>
Steven M. Haflich writes:

[...]

> A pretty printer encapsulating stream is a fairly big and complex
> object, and allocating one has consing and initialization costs.
> Therefore, it is a common strategy to treat them as a `resource'.

Ah, thanks, I hadn't realised you were referring to "reuse from one
global pprint entry to the next"; I was still thinking in terms of
"reuse within nested PPRINT-LOGICAL-BLOCKs".

[...]

> That's why the behavior you initially described seemed a simple bug.
> What you described was some sort of resource mechanism, but it was
> failing to reinitialize the output width to the current value of
> *pp-r-m* when an encapsulation stream is reused.

I think the reuse only occurs within nested PPRINT-LOGICAL-BLOCKs in
SBCL (and maybe CMUCL, haven't got it handy); a new stream is consed
for each complete pprint (if the original stream isn't a pretty
stream).  I know the following isn't conclusive due to pretty streams
having dynamic extent, but FWIW:

     (let ((printer nil))
       (pprint-logical-block (*standard-output* '())
         (setf printer *standard-output*))
       (pprint-logical-block (*standard-output* '())
         (and (eq printer *standard-output*)
              (print 'globally-eq))
         (let ((s *standard-output*))
           (pprint-logical-block (*standard-output* '())
             (and (eq s *standard-output*)
                  (print 'locally-eq))))))

prints 'LOCALLY-EQ.

While (as you say) possibly suboptimal, it doesn't seem to be buggy,
since *p-r-m* is used to init the pretty stream each time.  I wasn't
very clear on how and when the pretty stream was reused, hence the
confusion.  Apologies!

Thanks very much for clarifying how the pretty printer works,
-- 
'tony