[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Requested changes to R4RS

    Date: Fri, 26 May 89 14:01:43 edt
    From: Jonathan Rees <jar@zohar.ai.mit.edu>

    [Cc: Scheme-Standard deleted]

       Date: Thu, 25 May 89 02:19:56 -0700
       From: "Jonathan S. Shapiro" <shap@polya.stanford.edu>

       I think the peek-char debate centered around what peek-char's behavior
       was in the face of rubout handlers, which evolved from the discussions
       about char-ready?.  The problem is that if we can't define the
       semantics for char-ready?, a good argument can be made that for the
       same reasons we can't define the semantics of peek-char.  If it was
       there to be peeked at, and we can define what that means, then we can
       surely define what char-ready? means.

    We need some suitable primitive to permit the construction of portable
    readers.  Right now there is now way a user can write READ or anything
    similar because there is no way to find out when you've encountered
    the end of a symbol or number without gobbling the character that
    delimits then symbol or number.  I don't care what the primitive is.
    It doesn't have to be PEEK-CHAR, but we've got to have it.  Don't
    just argue against PEEK-CHAR; explain how we can solve the READ problem.

    I think CHAR-READY? is a much less interesting and important question.
    If it is made possible by progress on the READ problem, fine, but
    that's a secondary issue.

    What does X3J13 Common Lisp say about the interaction of PEEK-CHAR and
    input editing?

CLtL has wishy-washy enough wording that it doesn't bother me the way the
Scheme report's wording does.

The description of LISTEN (p380) gives a definition that talks in terms of
`chars available' and in the conditional about what READ-CHAR would do. The
description mostly addresses intent and doesn't make many concrete statements.

The description of PEEK-CHAR (p379) refers to the `next input' rather than
`next call to READ-CHAR' which allows a possible interpretation of CLEAR-INPUT
as an input operation, and so also leaves the wording consistent.

Two X3J13 issues address the behavior of PEEK-CHAR: PEEK-CHAR-READ-CHAR-ECHO
and UNREAD-CHAR-AFTER-PEEK-CHAR. Neither is directly relevant to the issue
at hand, but they are relevant to other things which perhaps the Scheme report
should be considering. 


 PEEK-CHAR-READ-CHAR-ECHO - Discussion of the echo relationship between
   PEEK-CHAR and READ-CHAR. Note that this started out being about both
   `echo streams' and `interactive streams' but X3J13 felt it was out of
   its jurisdiction to address `interactive streams,' so just went ahead
   and left that situation as (in my opinion) a mess. The final passed
   version addresses only echo streams.

 UNREAD-CHAR-AFTER-PEEK-CHAR - Addresses the need to prohibit an UNREAD-CHAR
   after PEEK-CHAR (with no intervening READ-CHAR) [on the same stream].


 Both proposals have been approved by X3J13; however, as with any decisions
 made by X3J13, they might be amended again by subsequent vote.  Further,
 they are not binding on CLtL -- the job of X3J13 is to write a3 ew standard,
 not to rewrite history by amending an existing one. As such, none of the
 decisions mentioned below are necessarily forced on any existing implementation.

Full write-ups follow...

References:   READ-CHAR (p379), UNREAD-CHAR (p379), PEEK-CHAR (p379),
	      MAKE-ECHO-STREAM (p330), Streams (p327-328),
Edit history: 06-Mar-88, Version 1 by Pitman,
              23-Jun-88, Version 2 by Pitman (remove interactive stuff)
               8-Oct-88, Version 3 by Pitman & Masinter
Status:       Approved by X3J13 on vote of 11-5: proposal FIRST-READ-CHAR
Problem Description:

  The interaction between PEEK-CHAR, READ-CHAR and streams made by
  MAKE-ECHO-STREAM is not made adequately clear about how many times
  a particular character may be echoed and at what time such echo
  is permissible.

  For example, given:
       (LET ((CHAR NIL))
	 (PEEK-CHAR)             (PRIN1 '---)
	 (PEEK-CHAR)             (PRIN1 '---)
	 (UNREAD-CHAR CHAR)      (PRIN1 '---)
  what is seen on the terminal? There are at least these possibilities:

  [1] PEEK-CHAR is implemented by READ-CHAR/UNREAD-CHAR. The first time
      a char is seen by READ-CHAR it's echoed, UNREAD-CHAR does not echo,
      re-fetching the char by READ-CHAR doesn't echo.

  [2] Characters are echoed whenever seen by PEEK-CHAR or READ-CHAR.
      Characters are not unechoed by UNREAD-CHAR.

  [3] Characters are not echoed by PEEK-CHAR but are echoed by READ-CHAR.
      No `unecho' action is done by UNREAD-CHAR.

  [4] PEEK-CHAR is implemented by READ-CHAR/UNREAD-CHAR.meEAD-CHAR echos
      but UNREAD-CHAR does not `unecho'.

  [5] PEEK-CHAR is implemented by READ-CHAR/UNREAD-CHAR. READ-CHAR echos
      but UNREAD-CHAR unechos (a magic Erase character must be 
      presupposed for display terminals, a file stream that can randomly
      access during output and/or back up must be presupposed for files,
      paper terminals just lose):

  [6] PEEK-CHAR is implemented by peeking and does not echo. The first
      time a char is seen by READ-CHAR it's echoed, UNREAD-CHAR does not
      echo, re-fetching the char by READ-CHAR doesn't echo:

  This list is not believed to be exhaustive. It is only to illustrate
  of the variety of possible ways in which the current specification can 
  be implemented without technically being in conflict with the written 
  word of CLtL. Obviously not all of these interpretations are considered 
  useful by all people, but usefulness has not been determined to be 
  criterial in satisfying the specification.

  The description of streams (p327-328) is also [probably deliberately]
  fuzzy on this issue as it relates to operating systems on which echoing 
  is done by the operating system. That is, some systems are line-at-a-time
  and all READ-CHAR and PEEK-CHAR operations happen after issues of echo
  have long since been resolved by a system call that reads and echos input 
  a line at a time. Other systems are character-at-a-time and these issues 
  hit home in a different way. It will probably be necessary to continue
  leaving things slightly unspecified in order to accomodate the native 
  style of the variety of operating systems now trying to support Common 
  Lisp, but we should be more up front about the game we are playing. (For
  example, code which must port between character-at-a-time and 
  line-at-a-time systems must be more careful about whether it does 
  newline-preceded or newline-terminated output than many CL programmers 
  might realize given the current wording.) Additionally, though, we should
  be on the lookout for less ambitious goals involving only partial
  compatibility to improve the situation wherever we can find a way to.

  are implicitly affected by any decisions made on this issue since their
  descriptions involve the use of UNREAD-CHAR and PEEK-CHAR, respectively.


  Ammend the description of READ-CHAR to say that when the stream is
  an echo stream (a stream created by MAKE-ECHO-STREAM), the character
  will be echoed on the stream the first time those characters are seen.
  (Characters which are not echoed by READ-CHAR are those which were
  put there by UNREAD-CHAR and hence are assumed to have been echoed
  already by a previous call to READ-CHAR.)

  Ammend the description of UNREAD-CHAR to say that when the stream
  is an echo stream (a stream created by MAKE-ECHO-STREAM), no attempt
  will be made to undo any echoing of the character which might already
  have been done on the stream. However, characters placed on the
  stream by UNREAD-CHAR will be marked in such as way as to inhibit
  later re-echo by READ-CHAR.

  Ammend the description of PEEK-CHAR to say that when the stream is
  an echo stream (a stream created by MAKE-ECHO-STREAM), characters
  which are only peeked at are not echoed. Note however that in the
  case that the PEEK-TYPE argument is not NIL, the characters which
  are passed by PEEK-CHAR are treated as if by READ-CHAR, and so are
  echoed unless they have been marked otherwise by READ-CHAR.

  Ammend the description of abstract input functions 
  that they are implicitly affected by these new echoing rules of 

  Note: This is consistent with behavior [6] in the problem description.

  Clarify that the echo behavior of interactive streams such as
  *TERMINAL-IO* continues to be implementation dependent.


  Although this is not known to be in use in any particular system,
  nothing prevents its use. It proposes a more rational interpretation
  of the echoing behavior of UNREAD-CHAR which might make it possible
  for programmers concerned about echo behavior not to have to shy
  away from UNREAD-CHAR. (It would probably also improve the behavior
  of READ-PRESERVING-WHITESPACE with regard to echoing, since its
  description mentions using UNREAD-CHAR.)

  Correct echoing behavior is important to programs which do batch
  processing, parsing, etc. Allowing multiple or premature echoing
  is clearly unsatisfactory.

Current Practice:

  A wide variety of behaviors are in use.

Cost to Implementors:


  The code to implement the proposed change itself is probably fairly

  In some operating systems, there may be echoing constraints which
  are overlooked here. 

  In some cases, there may be second order effects in the system 
  itself which would also require a somewhat less predictable amount 
  of work to fix. 

Cost to Users:

  Any change is effectively upward compatible since the previous
  behavior is so ill-specified.

  Most users probably naively expect (perhaps even without realizing
  it explicitly) that echoing will take care of itself. That is, they
  probably expect that echoing will occur at the time of the
  READ-CHAR and probably do not give a lot of thought to the effect
  of PEEK-CHAR. As such, FIRST-READ-CHAR probably best supports most 
  of their naive intuitions.

Cost of Non-Adoption:

  The streams returned by MAKE-ECHO-STREAM would continue to be
  significantly hard to use portably.


  A number of applications involving of parsers, batch script
  interpreters, and such would be possible to implement
  straightforwardly and portably.




  he feels it is more practically coherent. However, he says he has
  only mental exercises and no actual personal experience upon which
  to base that belief.

  Version 1 of this proposal treated interactive streams on par
  with echo streams, but while people agreed that this issue is
  a severe portability problem, some considered that the treatment
  of interactive streams got involved in operating system issues
  that were beyond the scope of the standard, so that part of the
  text was removed.
References:    pp 379, 380 of CLtL
Category:      CLARIFICATION
Edit history:  Version 1 by Doug Cutting <Cutting.PA@Xerox.COM> on July 29, 1988
               Version 2 by Masinter  2-Dec-88
Status:        Approved by X3J13: proposal DONT-ALLOW

Problem description:

PEEK-CHAR and UNREAD-CHAR are very similar mechanisms.  The description of
PEEK-CHAR in CLtL even states that "it is as if one had called READ-CHAR and
then UNREAD-CHAR in succession."  But while CLtL prohibits calling UNREAD-CHAR
twice in succession it does not prohibit calling UNREAD-CHAR after PEEK-CHAR.
The obvious implementation of PEEK-CHAR and UNREAD-CHAR (a one-character buffer)
will not work unless this prohibition is present.


   Rewrite the specification so that it is clear that doing either a
   PEEK-CHAR or READ-CHAR `commits' all previous characters. UNREAD-CHAR
   on any character preceding that which is seen by the PEEK-CHAR (including
   those passed over by PEEK-CHAR when `seeking' with a non-NIL first
   argument) is not specified.

   In particular, the results of calling  UNREAD-CHAR after PEEK-CHAR
   is unspecified.


   (defun test (&optional (stream *standard-input*))
     (let* ((char1a (read-char stream))	
	    (char2a (peek-char nil stream))
	    (char1b (progn (unread-char char1a stream)
			   (read-char stream)))
	    (char2b (read-char stream)))
       (list char1a char2a char1b char2b)))

This is not legal, since the PEEK-CHAR for char2a "commits"
the character read by char1a, and so the unread-char is not legal.


PEEK-CHAR and UNREAD-CHAR provide equivalent functionality and it is thus
reasonable for an implementation to implement them in terms of the same

Current practice:

In Xerox Common Lisp, different (non-random-access) stream types behave
differently. One, (TCP/FTP) handled this correctly, while another did not.

In Symbolics Genera, for the Example above:

     => (#\a #\b #\a #\b)

     (with-input-from-string (s "abc") (test s))
     => (#\a #\b #\a #\b)

     (progn (with-open-file (s "foo.output" :direction :output)
	      (write-string "abc" s))
            (with-open-file (s "foo.output" :direction :input) 
	      (test s)))
     Signals an error about unreading #\a when #\b was already unread.

Cost to Implementors:

Presumably none.  Implementations which allow this are still correct.

Cost to Users:

Small.  I suspect there is very little code which depends upon this working
correctly, as most code uses either PEEK-CHAR or UNREAD-CHAR, but not both.

Cost of non-adoption:

Implementations of sequential streams are forced to be unnecessarily complex in
order to be correct.


Allows simple yet adequately powerful implementation of sequential streams.


Requires that users have shared, one-char buffer model of how UNREAD-CHAR and
PEEK-CHAR work, rather than two separate one-char buffers.