CLHS: Issue STRING-OUTPUT-STREAM-BASHING Writeup

#

## Issue STRING-OUTPUT-STREAM-BASHING Writeup

    Issue:              STRING-OUTPUT-STREAM-BASHING
    References:         WITH-OUTPUT-TO-STRING
                        GET-OUTPUT-STREAM-STRING
                        FORMAT
    Related issues:     MAPPING-DESTRUCTIVE-INTERACTION
                        WITH-OUTPUT-TO-STRING-APPEND-STYLE
    Category:           CLARIFICATION
    Edit history:       V1, 12 Feb 1991, Sandra Loosemore

    Problem description:

      Is it valid to call GET-OUTPUT-STREAM-STRING on a stream bound by
      WITH-OUTPUT-TO-STRING?  If so, must the WITH-OUTPUT-TO-STRING still
      return a string containing *all* of the collected output to the
      stream?  Or should only output collected since the last call to
      GET-OUTPUT-STREAM-STRING be returned?

      The ambiguity exists for both the case where no string argument is
      provided to WITH-OUTPUT-TO-STRING (where the string is returned as its
      value) and where a string with a fill pointer is provided and
      destructively modified.  There is also a problem with string output
      streams created by calls to FORMAT when its stream argument is NIL or
      a string with a fill pointer, although users have to try harder to
      get their hands on the stream object in that case.

      It is also not clear what the effects of destructive modifications to
      a string supplied as an argument to WITH-OUTPUT-TO-STRING or FORMAT
      are supposed to be.

    Proposal (STRING-OUTPUT-STREAM-BASHING:UNDEFINED):

      Clarify that the consequences of calling GET-OUTPUT-STREAM-STRING
      on a stream created by WITH-OUTPUT-TO-STRING or FORMAT are undefined.
      In the cases where a string argument with a fill pointer is supplied
      as an argument to WITH-OUTPUT-TO-STRING or FORMAT, the consequences
      are undefined if destructive modifications are performed directly on
      the string during the dynamic extent of the call.

    Examples:

      #1: (with-output-to-string (s)
            (prin1 'foo s) (print (get-output-stream-string s))
            (prin1 'bar s) (print (get-output-stream-string s)))

          => Under proposal UNDEFINED, this program is in error.

      #2: (let ((x  (make-array 100 :element-type 'standard-char :fill-pointer 0)))
            (with-output-to-string (s x)
              (prin1 'foo s) (print (get-output-stream-string s))
              (prin1 'bar s) (print (get-output-stream-string s)))
            x)

          => Under proposal UNDEFINED, this program is in error.

      #3: (let ((x  (make-array 100 :element-type 'standard-char :fill-pointer 0)))
            (with-output-to-string (s x)
              (prin1 'foo s) (setf (fill-pointer x) 0)
              (prin1 'bar s) (setf (fill-pointer x) 0))
            x)

          => Under proposal UNDEFINED, this program is in error.

    Rationale:

      Calling GET-OUTPUT-STREAM-STRING is a destructive modification of
      the stream being manipulated by WITH-OUTPUT-TO-STRING or FORMAT.
      It would be consistent with issue MAPPING-DESTRUCTIVE-INTERACTION
      to prohibit this.  Likewise, some implementations become confused if
      the string with fill pointer underlying a string output stream is
      modified directly.

    Current Practice:

      Lucid, Allegro, and Symbolics Genera all print "FOO" and "BAR" and
      return "" from test case 1, but vary in the behavior of the other
      cases.

    Cost to Implementors:

      No implementation would be forced to change by this proposal.

    Cost to Users:

      Probably few user programs rely on any particular behavior here.

    Cost of non-adoption:

      Continuing vagueness in the language specification.

    Performance impact:

      N/A

    Benefits:

      The costs of non-adoption are avoided.

    Esthetics:

      It might be more esthetic to try to specify the behavior.  In the
      case of WITH-OUTPUT-TO-STRING where no string argument is provided
      and FORMAT with a stream argument of NIL, the behavior could easily
      be explained in terms of the obvious implementation of these constructs
      using MAKE-STRING-OUTPUT-STREAM and GET-OUTPUT-STREAM-STRING.  However,
      there are no corresponding primitives for creating a string stream from
      a string with a fill pointer.

    Discussion:

    -------

Copyright 1996-2005, LispWorks Ltd. All rights reserved.