Issue: STRING-OUTPUT-STREAM-BASHINGReferences: WITH-OUTPUT-TO-STRING
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:
-------