Received: from MIT-OZ by MIT-MC via Chaosnet; 12 MAR 85 18:51:29 EST Date: Tue 12 Mar 85 18:51:01-EST From: Gerald Jay Sussman Subject: Re: I/O proposal To: willc%indiana.csnet@CSNET-RELAY.ARPA cc: scheme@mit-mc.ARPA In-Reply-To: Message from "Will Clinger " of Tue 12 Mar 85 00:12:50-EST I think the I/O proposal is very nice. The only objection I have is the use of the word STREAM for the IO stuff. My problem is that we used that word all through chapter 3 of our book to mean the functional programming idea of (potentially) infinite sequence. It will be very painful to change that. I propose that the word be changed to one of CHANNEL, PIPE, PORT, TUBE, or any other appropriate euphemism. I believe that my use of the word STREAM is consistent with that of other authors on functional programming. -------  Received: from MIT-OZ by MIT-MC via Chaosnet; 12 MAR 85 17:39:56 EST Date: Tue, 12 Mar 1985 17:25 EST Message-ID: From: CPH%MIT-OZ@MIT-MC.ARPA To: Will Clinger Cc: scheme@MIT-MC.ARPA Subject: I/O proposal In-reply-to: Msg of 11 Mar 1985 23:20-EST from Will Clinger The I/O proposal seems quite good! In fact it is nearly identical to the current MIT Scheme I/O system, which makes me happy. I wonder why you chose the name "DISPLAY-CHAR" rather than "WRITE-CHAR"? I prefer "WRITE-CHAR" because it has no connotations of display terminals, or of being viewed by a human; it says, simply, that the character was written down somewhere. Also, I think that you should have both "STREAM?" and its components "INPUT-STREAM?" and "OUTPUT-STREAM?", because it makes more information available without really constraining the implementation in any fundamental way. Clearly bidirectional streams can satisfy both predicates. Chris Hanson  Received: from MIT-OZ by MIT-MC via Chaosnet; 12 MAR 85 16:03:00 EST Date: Tue 12 Mar 85 16:02:43-EST From: Gerald Jay Sussman Subject: Re: Numbers Committee Report To: mw%brandeis.csnet@CSNET-RELAY.ARPA cc: scheme@mit-mc.ARPA In-Reply-To: Message from "mw%brandeis.csnet@csnet-relay.arpa" of Tue 12 Mar 85 10:57:00-EST Three comments (of #I10e10): ***** I certainly hope there are fewer than that! 1. (/ z1 ... zi) ==> ((z1 / z2) ... / zi) -- since / is not associative, the order of operation should be specified explicitly. This is probably just an editing issue. ***** I agree and I have already fixed that. 2. Are formats list structures or some other kind of object? That is do you write (let ((format (cons 'FLO '(13)))) (number->string z format)) or is FLO some kind of functional object? ***** My initial response is that formats are simple list structures -- there may be some format interpreter, but it should not be part of Scheme. Presumably this can be later generalized and simplified, if someone wants to go through the trouble, but I would be happy with the dumb idea. 3. In paragraph K, are real, integer, etc supposed to be functions or keywords? That is, can you write (let ((range real)) ((range sqrt) x)) ? In either case, this proposal ties up those identifiers in a pretty complicated way. I am not sure how bad this is, but it deserves thought. ***** The type-restriction operations are intended to be scheme procedures which take procedures as arguments and which produce procedures as values. They are bound in the initial environment to the operator symbols REAL, INTEGER, etc. They may be passed as arguments, etc. Thus, this does not tie up the identifiers at all, and the example you give shows that. -------  Received: from csnet-relay by MIT-MC.ARPA; 12 MAR 85 12:52:56 EST Received: from brandeis by csnet-relay.csnet id ag08006; 12 Mar 85 12:39 EST Received: by brandeis.ARPA (4.12/4.7) id AA02554; Tue, 12 Mar 85 12:08:14 est Date: 12 Mar 1985 11:22-EST From: mw%brandeis.csnet@csnet-relay.arpa In-Real-Life: Mitchell Wand,faculty Subject: Re: I/O proposal To: scheme@mit-mc.ARPA Message-Id: <479492551/mw@brandeis> A few remarks: 1. LOAD does not specify how it interacts with CURRENT-INPUT-STREAM or CURRENT-OUTPUT-STREAM while loading. What is to be the preferred way of loading a bootstrap of the following form: (define! foo ...) (define! bar (foo (read))) ..complicated data to be processed by foo during load.. It might be argued that this is bad style, but I am not convinced: such a bootstrap is less dependent on operating system file naming conventions than one with the data in a separate file. 2. "A different set of procedures will be needed to create streams from and to strings, and the closing procedures should still work on them. It seems that closing a string output stream should yield a string, but it isn't clear what should be returned when a file output stream is closed." I don't understand this at all. The phrase "create streams from and to strings" is rather difficult to parse, which doesn't help. 3. Are with-input-from-file and with-output-from-file the only way to change the value of (current-input-stream) and (current-output-stream) ? If so, shouldn't they be essential ? -- Mitch Wand  Received: from csnet-relay by MIT-MC.ARPA; 12 MAR 85 12:39:35 EST Received: from brandeis by csnet-relay.csnet id ac08006; 12 Mar 85 12:36 EST Received: by brandeis.ARPA (4.12/4.7) id AA01743; Tue, 12 Mar 85 11:08:41 est Date: 12 Mar 1985 10:57-EST From: mw%brandeis.csnet@csnet-relay.arpa In-Real-Life: Mitchell Wand,faculty Subject: Re: Numbers Committee Report To: scheme@mit-mc.ARPA Message-Id: <479491071/mw@brandeis> Three comments (of #I10e10): 1. (/ z1 ... zi) ==> ((z1 / z2) ... / zi) -- since / is not associative, the order of operation should be specified explicitly. This is probably just an editing issue. 2. Are formats list structures or some other kind of object? That is do you write (let ((format (cons 'FLO '(13)))) (number->string z format)) or is FLO some kind of functional object? 3. In paragraph K, are real, integer, etc supposed to be functions or keywords? That is, can you write (let ((range real)) ((range sqrt) x)) ? In either case, this proposal ties up those identifiers in a pretty complicated way. I am not sure how bad this is, but it deserves thought. -- Mitch Wand  Received: from csnet-relay by MIT-MC.ARPA; 11 MAR 85 23:49:17 EST Received: from indiana by csnet-relay.csnet id ac04021; 11 Mar 85 23:37 EST Date: Mon, 11 Mar 85 23:20:31 est From: Will Clinger Received: by iuvax.UUCP; id AA14898; Mon, 11 Mar 85 23:20:31 est To: scheme@mit-mc.ARPA Subject: I/O proposal From Gary Brooks and William Clinger at Indiana U: This is an interim proposal. It takes an old-fashioned view of streams, and it does nothing about windows and graphics. Arguments to functions are delimited by angle brackets (<...>) and optional arguments are further delmited by curly brackets ({...}). (eof? ) Essential EOF? takes an object and returns true if the object is an end of file object. The precise set of end of file objects will vary among implementations, but in any case no end of file object will ever be a character or an object that can be read in using READ. Rationale: A fixed set of end of file objects seems to be simpler than allowing each input procedure to take yet another optional argument specifying what to do on end of file. Allowing for a set of such objects rather than a single such object allows for different implementation strategies. (read {}) Essential READ takes an input stream as argument and returns the next object parsable from the stream, updating the stream to point to the first character past the end of the written representation of the object. If an end of file is encountered in the input before any characters are found that can begin an object, then an end of file object is returned. If an end of file is encountered after the beginning of an object's written representation, but the written representation is incomplete and therefore not parsable, an error must be signalled. The stream argument to READ may be omitted, in which case it defaults to (CURRENT-INPUT-STREAM). Rationale: READ corresponds to Common Lisp's READ-PRESERVING-WHITESPACE. This will require something like UNREAD-CHAR or PEEK-CHAR internally. For simplicity, it is never an error to encounter end of file when READ is called. (write {}) Essential WRITE takes an object and an output stream and writes a representation of the object to the stream. Strings that appear in the written representation are enclosed in double quotes, and within those strings backslash and double quote characters are escaped by backslashes. (Implementations that allow slashification within symbols will probably want WRITE to slashify funny characters in symbols as well.) WRITE returns an unspecified value. The stream argument to WRITE may be omitted, in which case it defaults to (CURRENT-OUTPUT-STREAM). (display {}) Essential DISPLAY takes an object and an output stream and writes a representation of the object to the stream. Strings that appear in the written representation are not enclosed in double quotes, and no characters are escaped within those strings. DISPLAY returns an unspecified value. The stream argument to DISPLAY may be omitted, in which case it defaults to (CURRENT-OUTPUT-STREAM). Rationale: WRITE is for producing machine-readable output, DISPLAY for producing human-readable output. (newline {}) Essential NEWLINE takes an output stream and writes a newline to the stream. NEWLINE returns an unspecified value. The stream argument to NEWLINE may be omitted, in which case it defaults to (CURRENT-OUTPUT-STREAM). Rationale: NEWLINE abstracts away from the implementation details of ending a line (or is it starting a new one?). (listen? {}) Optional LISTEN? takes an input stream (an interactive stream in the useful case) and returns true if a character is ready on that stream so that a READ-CHAR operation will not hang and returns false if a READ-CHAR operation will hang. If the stream is at end of file then the value returned by LISTEN? is unspecified. Rationale: LISTEN? is needed for interactive input streams. (read-char {}) Essential READ-CHAR takes an input stream and returns the next character available from the stream, updating the stream to point to the following character. If no more characters are available from the stream, an end of file value is returned. The stream argument to READ-CHAR may be omitted, in which case it defaults to (CURRENT-INPUT-STREAM). (display-char {}) Essential DISPLAY-CHAR takes a character and an output stream, and writes the character itself (not a written representation of the character) to the stream. DISPLAY-CHAR returns an unspecified value. The stream argument to DISPLAY-CHAR may be omitted, in which case it defaults to (CURRENT-OUTPUT-STREAM). Rationale: READ-CHAR and DISPLAY-CHAR are for low-level I/O. (load ) Essential LOAD takes a string that names an existing file containing Scheme source code. It reads Scheme expressions from the file and interprets them sequentially as though they had been typed interactively. It is not specified whether the results of the expressions are printed. Neither is it specified whether or not the LOAD procedure affects the values returned by (CURRENT-INPUT-STREAM) and (CURRENT-OUTPUT-STREAM) during the loading process. LOAD returns an unspecified value. Rationale: For portability LOAD must operate on source files. Its operation on other kinds of files necessarily varies among implementations. (transcript-on ) Optional TRANSCRIPT-ON takes a string that names an output file to be created, and returns an unspecified value. The effect of TRANSCRIPT-ON is to open the named file for output, and to cause a transcript of subsequent interaction between the user and the Scheme system to be written to the file. The transcript is ended by a call to TRANSCRIPT-OFF. Only one transcript may be in progress at any time. (Some implementations may relax this restriction.) (transcript-off) Optional TRANSCRIPT-OFF takes no arguments and returns an unspecified value. It ends any transcript in progress and closes the transcript file. Rationale: TRANSCRIPT-ON and TRANSCRIPT-OFF are redundant in some systems, but systems that need them should provide them. (stream? ) Essential STREAM? takes one argument and returns true iff its argument is a stream. Rationale: Perhaps STREAM? should be subdivided into INPUT-STREAM? and OUTPUT-STREAM?. (current-input-stream) Essential CURRENT-INPUT-STREAM takes no arguments and returns the current default input stream. (current-output-stream) Essential CURRENT-OUTPUT-STREAM takes no arguments and returns the current default output stream. Rationale: CURRENT-INPUT-STREAM and CURRENT-OUTPUT-STREAM are procedures rather than variables to allow for various ways to implement the default streams. Explicit fetching of the default streams is convenient for programs that want to change the default stream using WITH-INPUT-FROM-FILE or WITH-OUTPUT-TO-FILE and still use the original default stream for some I/O; see below. (call-with-input-file ) Essential CALL-WITH-INPUT-FILE takes a procedure of one argument and a string naming an existing file and calls the procedure with the stream obtained by opening the named file for input. If the file cannot be opened, an error should be signalled. If the procedure returns, then the stream is closed automatically and the value yielded by the procedure is returned by CALL-WITH-INPUT-FILE. If the current continuation ever changes in such a way as to make it doubtful that the procedure will return, CALL-WITH-INPUT-FILE may close the input stream. The exact interaction of escape procedures with CALL-WITH-INPUT-FILE is unspecified. (call-with-output-file ) Essential CALL-WITH-OUTPUT-FILE takes a procedure of one argument and a string naming a file to be created and calls the procedure with the stream obtained by opening the named file for output. If the file cannot be be opened, an error should be signalled. If a file with that name already exists, the effect is unspecified. If the procedure returns, then the stream is closed automatically and the value yielded by the procedure is returned by CALL-WITH-OUTPUT-FILE. If the current continuation ever changes in such a way as to make it doubtful that the procedure will return, CALL-WITH-OUTPUT-FILE may close the output stream. The exact interaction of escape procedures with CALL-WITH-OUTPUT-FILE is unspecified. Rationale: CALL-WITH-INPUT-FILE and CALL-WITH-OUTPUT-FILE are convenient for programs that need to read input from or send output to several directions at once. They cannot be synthesized from WITH-INPUT-FROM-FILE and WITH-OUTPUT-TO-FILE so they are the essential procedures. Whether or not they close files when there is a throw out of the procedure is mainly a performance issue, of greatest importance when there is a small limit on the number of files that can be open at once. If they close files for a throw, then the vagueness of what it means for the current continuation to change in such a way as to make it doubtful that a procedure will return, the possibility of a throw back in, and the vagueness of what it means to close a file, make it unwise to specify the exact interaction of the file-closing mechanism with escape procedures. It is arguable whether the notion of closing a file should be reflected in the semantics of Scheme. Operating systems require that files be closed for the following reasons: 1. To reclaim storage. This should be left to the garbage collector. 2. To prevent some table in the operating system from overflowing because there are too many files open at once. Only implementations that have this problem should have to worry about it. 3. To facilitate file locking. This seems to be the only semantic reason for closing a file, and even so there are implementations for which it isn't meaningful. Because some systems have to worry about closing files, however, all portable Scheme code must worry about it. We have tried to follow MIT's lead in isolating the worry within a few standard procedures. (open-input-file ) Optional OPEN-INPUT-FILE takes a string naming an existing file and returns an input stream capable of delivering characters from the file. If the file cannot be opened, an error should be signalled. (open-output-file ) Optional OPEN-OUTPUT-FILE takes a string naming an output file to be created and returns an output stream capable of writing characters to a new file by that name. If the file cannot be opened, an error should be signalled. If a file with the given name already exists, the effect is unspecified. (close-input-stream ) Optional CLOSE-INPUT-STREAM takes an input stream and returns an unspecified value. If the stream is connected to a file, the file is closed and the stream rendered incapable of delivering characters. (close-output-stream ) Optional CLOSE-OUTPUT-STREAM takes an output stream and returns an unspecified value. If the stream is connected to a file, the file is closed and the stream rendered incapable of writing characters to it. Rationale: OPEN-INPUT-FILE, OPEN-OUTPUT-FILE, CLOSE-INPUT-STREAM, and CLOSE-OUTPUT-STREAM are needed to use streams whose lifetimes are not properly nested, which is to say they are seldom needed. OPEN-INPUT-FILE and OPEN-OUTPUT-FILE are distinct to avoid the need for a switch argument; if independent switches were required, it would be better to use switches rather than have a product space of procedures. CLOSE-INPUT-STREAM and CLOSE-OUTPUT-STREAM could probably be combined into CLOSE-STREAM with no great loss. A different set of procedures will be needed to create streams from and to strings, and the closing procedures should still work on them. It seems that closing a string output stream should yield a string, but it isn't clear what should be returned when a file output stream is closed. (with-input-from-file ) Optional WITH-INPUT-FROM-FILE takes a string and a thunk (a procedure of no arguments). The string must name an existing file. The file is opened for input, an input stream connected to it is made the default value returned by (CURRENT-INPUT-STREAM), and the thunk is invoked. When the thunk returns, (CURRENT-INPUT-STREAM) is closed and the previous default value returned by (CURRENT-INPUT-STREAM) is restored. WITH-INPUT-FROM-FILE returns the value returned by the thunk. Furthermore WITH-INPUT-FROM-FILE should attempt to close the input stream and restore the default input stream whenever the current continuation changes in such a way as to make it doubtful that the thunk will ever return. (with-output-to-file ) Optional WITH-OUTPUT-TO-FILE takes a stfung and a thunk. The string names an output file to be created. The file is opened for output, an output stream connected to it is made the default value returned by (CURRENT-OUTPUT-STREAM), and the thunk is invoked. When the thunk returns, (CURRENT-OUTPUT-STREAM) is closed and the previous default value returned by (CURRENT-OUTPUT-STREAM) is restored. WITH-OUTPUT-FROM-FILE returns the value returned by the thunk. Furthermore WITH-OUTPUT-FROM-FILE should attempt to close the output stream and restore the default output stream whenever the current continuation changes in such a way as to make it doubtful that the thunk will ever return. It has been suggested that if evaluation of the thunk is abandoned and later continued then the file should be re-opened in exactly the same state that it had been in when it was last closed, but this may be awkward or inefficient in some implementations. Rationale: WITH-INPUT-FROM-FILE and WITH-OUTPUT-TO-FILE are sufficient for programs that only use one input or one output at a time. Indeed by nesting them and by fetching the various nested streams using CURRENT-INPUT-STREAM and CURRENT-OUTPUT-STREAM, arbitrarily many input or output files may be used. The automatic closing of the streams may not be terribly important in some implementations, but the fact that the old defaults are restored is important semantically. There are several incompatible ways that they could interact with escape procedures, however, and at this time it isn't entirely clear which ways are best. See rationale for CALL-WITH-OUTPUT-FILE and CALL-WITH-INPUT-FILE.  Received: from MIT-EECS by MIT-MC via Chaosnet; 10 MAR 85 16:53:21 EST Date: Sun 10 Mar 85 16:53:46-EST From: GJS%MIT-EECS@MIT-MC.ARPA Subject: Numbers Committee Report To: scheme@MIT-MC Sorry it took so long, but I was afraid to do a half-assed job. The following report raises 10^10 issues which should be thought about by all of us. I believe (after a bit of study) that other language people have been inadequately careful about the status of numbers in their languages. Numbers are probably the most complicated data structures we ever deal with, so we must try to do a good job on them. The report is entirely the responsibility of GJS (so throw stones at him), though Bill Rozas, Jon Rees, Guy Steele, Chris Hanson, Hal Abelson, Yekta Gursel, and Chris Lindblad have made significant contributions -- most of their stones have been thrown and absorbed (some hurt!). Scheme Numbers Intent Numerical computation has traditionally been neglected by the Lisp community. Until Common Lisp there has been no carefully thought out strategy for organizing numerical computation; and with the exception of the MacLisp system there has been little effort to efficiently execute numerical code. We applaud the excellent work of the Common Lisp committee and we accept many of their recommendations. In some ways we simplify and generalize their proposals in a manner consistent with the purposes of Scheme. Scheme numerical operations treat numbers as abstract data, as independent of the machine representation as is possible. Thus, the casual user should be able to write simple programs without having to know that the implementation may use fixed-point, floating-point, and perhaps other representations for his data. Unfortunately, this illusion of uniformity can only be sustained approximately -- the implementation of numbers will leak out of our abstraction whenever the user must be in control of precision, or accuracy, or when he must construct especially efficient computations. Thus we also must provide escape mechanisms so that a sophisticated programmer can exercise more control of the execution of his code and the representation of his data when it is essential to do so. We separate out several apparently related issues of representation -- the abstract numbers, their machine representation, and the input/output formats of the symbolic expressions which denote numerical constants. We will use mathematical words such as NUMBER, COMPLEX, REAL, RATIONAL, and INTEGER for properties of the abstract numbers, data-structure names such as FIXNUM, BIGNUM, RATNUM, and FLONUM for machine representations, and names like INT, FIX, FLO, SCI, RAT, POLAR, and RECT for input/output formats. Numbers A Scheme system provides data of type NUMBER, which is the most general numerical type supported by that system. NUMBER is likely to be a complicated union type implemented in terms of FIXNUMs, BIGNUMS, FLONUMS, etc. but this should not be apparent to a naive user. What the user sees is that the obvious operations on numbers produce the mathematically expected results, within the limits of the implementation. Thus if the user divides 3 by 2, he should get something like 1.5 (or, the exact fraction 3/2). If he adds that result to itself, and if the implementation can swing it, he should get an exact 3. Mathematically, numbers may be arranged into a tower of subtypes with natural projections and injections relating adjacent members of the tower: NUMBER COMPLEX REAL RATIONAL INTEGER We impose a uniform rule of downward coercion -- a number of one type is also of a lower type if the injection (up) of the projection (down) of a number leaves the number unchanged. Since this tower is a real mathematical entity, Scheme provides predicates and procedures that access the tower. Not all Scheme implementations must provide the whole tower, but they are required to implement a coherent subset, consistent with the purposes of Scheme. Exactness Numbers are either EXACT or INEXACT. A number is exact if it was derived from EXACT numbers using only EXACT operations. A number is INEXACT if it models a quantity known only approximately, if it was derived using INEXACT ingredients, or if it was derived using INEXACT operations. Thus INEXACTness is a contagious property of a number. Some operations, such as the square root (of non-square numbers) must be INEXACT because of the finite precision of our representations. Other operations are inexact because of implementation requirements. We emphasize that exactness is independent of the position of the number on the tower. It is perfectly possible to have an INEXACT INTEGER or an EXACT REAL; 355/113 may be an EXACT RATIONAL or it may be an INEXACT RATIONAL approximation to pi, depending on the application. Operationally, it is the system's responsibility to combine EXACT numbers using exact methods, such as infinite precision integer and rational arithmetic, where possible. An implementation may not be able to do this (if it does not use infinite precision integers and rationals), but if a number becomes inexact for implementation reasons there is probably an important error condition, such as integer overflow, to be reported. Arithmetic on INEXACT numbers is not so constrained. The system may use floating point and other flaky representation strategies for INEXACT numbers. This is not to say that implementors need not use the best known algorithms for INEXACT computations -- only that high-quality approximate methods are allowed. In a system which cannot explicitly distinguish exact from inexact numbers the system must do its best to maintain precision. Scheme systems must not burden users with numerical operations described in terms of hardware and operating-system dependent representations such as FIXNUM and FLONUM. These representation issues should not be germane to the user's problems. We highly recommend that the IEEE 32-bit and 64-bit floating-point standards be adopted for implementations that use floating-point internal representations. To minimize loss of precision we adopt the following rules: If an implementation uses several different size floating-point formats, the results of any operation with a floating-point result must be expressed in the largest format used to express any of the floating-point arguments to that operation. It is desirable (but not required) for potentially irrational operations such as SQRT, when applied to EXACT arguments, to produce EXACT answers when that is possible (eg. sqrt[4]=2, exactly). If an EXACT number (or an INEXACT number represented as a FIXNUM, a BIGNUM, or a RATNUM) is operated upon so as to produce an INEXACT result (as by SQRT), then if the result is represented as a FLONUM, then the largest available format FLONUM must be used; but if the result is expressed as a RATNUM then the rational approximation must have at least as much precision as the largest available format FLONUM. (Note that this last rule is much stronger than that used in Common Lisp. Consider the result of the square root of a rational, for example.) Numerical Operations Scheme provides the usual set of operations for manipulation of numbers. In the specification below we illustrate each built in numerical operation with an expression using the default operator symbol which is bound to that operation in the global environment. In general, numerical operations require numerical arguments. We use the following symbols to represent the various types of object in our descriptions. It is an error for an operation to be presented with an argument that it is not specified to handle. obj any arbitrary object z, z1, ... zi, ... {complex, real, rational, integer} x, x1, ... xi, ... {real, rational, integer} q, q1, ... qi, ... {rational, integer} n, n1, ... ni, ... {integer} In our descriptions some arguments are required and others are optionally allowed. For example, numerical comparisons are allowed to have any number of arguments, but they must have at least two. We indicate this by putting in explicit argument symbols for each required argument and a "..." indicating the rest. A. Numerical type predicates can be applied to any kind of argument. They return true if the object is of the named type. In general, if a type predicate is true of a number then all higher type predicates are also true of that number. Not every system supports all of these types. It is entirely possible to have a Scheme system which only has INTEGERs, however, it must have all of these predicates. (NUMBER? obj) (COMPLEX? obj) (REAL? obj) (RATIONAL? obj) (INTEGER? obj) B. Numerical predicates test a number for a particular property. They return a boolean value. (ZERO? z) (POSITIVE? x) (NEGATIVE? x) (ODD? n) (EVEN? n) (EXACT? z) (INEXACT? z) C. Numerical comparisons require all of their arguments to be numbers. They optionally take many arguments, as in Common Lisp, to facilitate encoding of range checks. Not all implementations support this extension. These predicates have redundant names (with and without the terminal "?") to make all populations happy. Warning: On INEXACT numbers equality tests will give unreliable results; other numerical comparisons are only heuristically useful (ask a numerical analyst about this!) (= z1 z2 ... zi) all arguments are numerically equal (=? z1 z2 ... zi) (< x1 x2 ... xi) arguments are monotonically increasing ( x1 x2 ... xi) arguments are monotonically decreasing (>? x1 x2 ... xi) (<= x1 x2 ... xi) arguments are monotonically nondecreasing (<=? x1 x2 ... xi) (>= x1 x2 ... xi) arguments are monotonically nonincreasing (>=? x1 x2 ... xi) (max x1 ... xi) (min x1 ... xi) D. Arithmetic operations require all arguments to be numbers. Generalization to arbitrary numbers of arguments is implementation dependent. (+ z1 ... zi) ==> z1 + ... + zi (+ z) ==> z (+) ==> 0 (the identity) (* z1 ... zi) ==> z1 * ... * zi (* z) ==> z (*) ==> 1 (the identity) (- z1 ... zi) ==> z1 - z2 - ... - zi (- z) ==> -n (/ z1 ... zi) ==> z1 / z2 / ... / zi (/ z) ==> 1/z (-1+ z) ==> -1 + z (1+ z) ==> 1 + z (abs z) E. Integer division operations are only defined on integers. (quotient n1 n2) ==> n3 (remainder n1 n2) ==> n4 (modulo n1 n2) ==> n4 In general, these are intended to implement number-theoretic division, where for positive arguments, n1 and n2 n1 = n3*n2 + n4 and 0 <= n4 < d. REMAINDER and MODULO differ on negative arguments as in the Common Lisp REM and MOD procedures -- the remainder always has the sign of the dividend, the modulo always has the sign of the divisor: (modulo 13 4) ==> 1 (remainder 13 4) ==> 1 (modulo -13 4) ==> 3 (remainder -13 4) ==> -1 (modulo 13 -4) ==> -3 (remainder 13 -4) ==> 1 (modulo -13 -4) ==> -1 (remainder -13 -4) ==> -1 F. The following procedures are also only defined on integers. The result is always non-negative. (gcd n1 ... ni) Greatest common divisor (gcd) ==> 0 (the identity) (lcm n1 ... ni) Least common multiple (lcm) ==> 1 (the identity) G. Integers and rationals can be made using the following procedures. Note, this does not make the answer EXACT -- in fact, the resulting number is clearly INEXACT, it can be made EXACT with an exactness coercion. (floor x) The largest integer not larger than x (ceiling x) The smallest integer not smaller than x (truncate x) The integer of maximal absolute value not larger than x (round x) The closest integer to n. Rounds to even when halfway. (rationalize x y) Produces the best rational approximation to x within the tolerance specified by y. (rationalize x) Produces the best rational approximation to x, preserving all of the precision in its representation. H. In implementations which support real numbers the following are defined to conform with the Common Lisp standard as to meaning (be careful of the branch cuts if complex numbers are allowed). (exp z) (log z) (expt z1 z2) (sqrt z) (sin z) (cos z) (tan z) (asin z) (acos z) (atan z1 z2) I. In implementations which support complex numbers we also provide (make-rectangular x1 x2) ==> x1 + x2i (make-polar x3 x4) ==> x3*e^x4i (real-part z) (imag-part z) (magnitude z) (angle z) J. Exactness coercions. (exact->inexact z) Makes an inexact representation of z. Pretty harmless. (inexact->exact z) Makes an exact representation of z. I hope you know what you are doing here! K. Type-restricted operations are useful for error checking, or as advice to a compiler. Scheme optionally supplies such type-restricted operations: If op is defined on objects of numerical type, t1, and if t2 is any type lower on the tower than t1, then (t2 op) is the restricted operator for objects of type t2. For example, the following are expressions using restricted operators: ((real sqrt) 5) ; Gets the right thing. ((real sqrt) -1) ; Will signal an error. ((integer sqrt) 10) ; Will signal an error -- You probably wanted to compute (truncate ((real sqrt) 10)) For completeness, the system also supplies the most general operations ((number +) 3 4) The default operations, initially bound to the customary operators in the global environment, are these most general operations. The type-restriction procedures REAL, INTEGER, ... are simple maps which map the default operations to their restricted cousins. Some systems may want to enhance this facility, to allow integer subranges or other mathematically meaningful restrictions such as modular arithmetic, for example: (((integer-subrange 3 5) +) 2 3 4) ;Will signal an error. Numerical Input and Output Scheme allows all of the traditional ways of expressing numerical constants, though only some may be supported by any particular implementation. These expressions are intended to be purely notational; any kind of number may be expressed in any form that the user deems convenient. Of course, expressing 1/7 as a limited-precision decimal fraction will not exactly express the number, but this approximate expression may be just what the user wants to see. The expressions of numerical constants can be specified using formats. The system provides a procedure, NUMBER->STRING, which takes a number and a format and which produces a string which is the printed expression of the given number in the given format. (number->string ) ==> This procedure will mostly be used by sophisticated users and in system programs. In general, a naive user will need to know nothing about the formats because the system printer will have reasonable default formats for all types of NUMBERs. The system reader will construct reasonable default numerical types for numbers expressed in each of the formats it recognizes. If a user needs control of the coercion from strings to numbers he will use STRING->NUMBER, which takes a string, an exactness, and a radix and which produces a number of the maximally precise applicable type expressed by the given string. (string->number E|I B|O|D|X) ==> Formats may have parameters. For example, the "(SCI 5 2)" format specifies that a number is to be expressed in FORTRAN scientific format with 5 significant places and two places after the radix point. The following are all numerical constant expressions. The comment shows the format that was used to produce the expression: 123 +123 -123 ; (INT) format 12345678901234567890123456789 ; A big one 355/113 -355/113 +355/113 ; (RAT) format +123.45 -123.45 ; (FIX 2) format 3.14159265358979 ; (FIX 14) format 3.14159265358979 ; (FLO 15) format 123.450 ; (FLO 6) format -123.45E-1 ; (SCI 5 2) format 123E3 123E-3 -123E-3 ; (SCI 3 0) format -1+2i ; (RECT (INT) (INT)) format 1.2@1.570796 ; (POLAR (FIX 1) (FLO 7)) format A numerical constant may be specified with an explicit radix by a prefix. The prefixes are: #B (binary), #O (octal), #D (decimal), #X (hex). A format may specify that a number be expressed in a particular radix. The radix prefix may be suppressed. For example, one may express a complex number in polar form with the magnitude in octal and the angle in decimal as follows: #o1.2@#d1.570796327 ;(POLAR (FLO 2 (RADIX O)) (FLO (RADIX D))) format #o1.2@1.570796327 ;(POLAR (FLO 2 (RADIX O)) (FLO (RADIX D S)) format A numerical constant may be specified to be either EXACT or INEXACT by a prefix. The prefixes are: #I (inexact), #E (exact). An exactness prefix may appear before or after any radix prefix that is used. A format may specify that a number be expressed with an explicit exactness prefix, or it may force the exactness to be suppressed. For example, the following are ways to output an inexact value for pi: #I355/113 ; (RAT (EXACTNESS)) 355/113 ; (RAT (EXACTNESS S)) #I3.1416 ; (FIX 4 (EXACTNESS)) An attempt to produce more digits than are available in the internal machine representation of a number will be marked with a "#" filling the extra digits. This is not a statement that the implementation knows or keeps track of the significance of a number, just that the machine will flag attempts to produce 20 digits of a number which has only 15 digits of machine representation: 3.14158265358979##### ; (FLO 20 (EXACTNESS S)) In systems with both single and double precision FLONUMs one may want to specify which size we want to use to internally represent a constant. For example, we may want a constant whidh is pi rounded to the single precision length, or we might want a long number which has the value 6/10. In either case, we are specifying an explicit way to represent an INEXACT number. For this purpose, we allow one to express a number with a prefix which indicates short or long FLONUM representation: #S3.14159265358979 ; Round to short -- 3.141593 #L.6 ; Extend to long -- .600000000000000 Details of formats The format of a number is notated as a list beginning with a format descriptor, such as SCI. Following the descriptor are parameters used by that descriptor, such as the number of significant digits to be used. Parameters which are omitted are defaulted. Next, one may specify modifiers, such as RADIX or EXACTNESS, which themselves may be parameterized. The format descriptors are: (INT) Express as an integer. The radix point is implicit. If there are not enough significant places, as in trying to express 6.0238E23 (internally represented as a 7 digit FLONUM) as an integer we would get "6023800################". (RAT n) Express as a rational fraction. n specifies the largest denominator to be used in constructing a rational approximation to the number being expressed. If n is omitted it defaults to infinity. (FIX n) Express with a fixed radix point. n specifies the number of places to right of the radix point. n defaults to the size of a single-precision FLONUM. If there are not enough significant places, as in trying to express 6.0238E23 (internally represented as a 7 digit FLONUM) as a (FIX 2) we would get "6023800################.##". (FLO n) Express with a floating radix point. n specifies the total number of places to be displayed. n defaults to the size of a single- precision FLONUM. If the number is out of range, it is converted to (SCI). (FLO H) allows the system to heuristically express a FLO for human consumption (as in MacLisp printer). (SCI n m) Express in exponential notation. n specifies the total number of places to be displayed. n defaults to the size of a single- precision FLONUM. m specifies the number of places to the right of the radix point. m defaults to n-1. (SCI H) does heuristic expression. (RECT r i) Express as a rectangular form complex number. r and i are formats for the real and imaginary parts respectively. They default to (HEUR). (POLAR m a) Express as a polar form complex number. m and a are formats for the magnitude and angle respectively. m and a default to (HEUR). (HEUR) Express heuristically, as in the MacLisp printer (see Steele), using the minimum number of digits required to get an expression which when coerced back to a number produces the original machine representation. EXACT numbers are expressed as (INT) or (RAT). INEXACT numbers are expressed as (FLO H) or (SCI H) depending on their range. Complex numbers are expressed in (RECT). This is the normal default of the system printer. The following modifiers may be added to a numerical format specification: (EXACTNESS s) This controls the expression of the exactness label of a number. s indicates whether the exactness is to be E (expressed) or S (suppressed). s defaults to E. If no exactness modifier is specified for a format the exactness is by default not expressed. (RADIX r s) This forces a number to be expressed in the radix r. r may be B (binary), O (octal), D (decimal), or X (hex). s indicates whether the radix label is to be E (expressed) or S (suppressed). s defaults to E. If no radix modifier is specified the default is decimal and the label is suppressed. -------  Received: from indiana by csnet-relay.csnet id ad22218; 21 Feb 85 15:20 EST Date: Thu, 21 Feb 85 13:14:25 est From: Will Clinger Received: by iuvax.UUCP; id AA04423; Thu, 21 Feb 85 13:14:25 est To: scheme@mit-mc.ARPA Subject: Draft delayed to 15 March The draft of the final report on the Brandeis workshop, which was to have come out around 1 March, will be delayed to the vicinity of 15 March. I will be travelling until 4 March and probably won't be able to finish the draft until I get back. I am sorry for the delay. William Clinger Indiana University  Date: 5 February 1985 13:08-EST From: Jonathan A Rees Subject: Scheme String Operations To: SCHEME @ MIT-MC In-reply-to: Msg of Sun 3 Feb 85 11:59:27 est from Kent Dybvig I should point out that there were people at the workshop who felt strongly that strings should not be mutable. I don't much care one way or the other. If the things which use the "..." syntax are immutable then there ought to be such a thing as vector-of-character; also, if strings are immutable then it's not clear how they are different from symbols (maybe just that they self-evaluate). But those of you who strongly think (or thought) that strings should be immutable should speak up. Jonathan  Date: 5 February 1985 13:03-EST From: Jonathan A Rees Subject: string->symbol, symbol->string To: dyb%unc.csnet @ CSNET-RELAY cc: SCHEME @ MIT-MC In-reply-to: Msg of Sun 3 Feb 85 12:00:27 est from Kent Dybvig Date: Sun, 3 Feb 85 12:00:27 est From: Kent Dybvig "symbol->string" and "string->symbol functions are not analogous to "list->string" or "string->list". "list->string" converts the same data from one representation into another, with no loss of information. The same is true for "string->list", "list->vector", "vector->list" and "character->integer". There definitely is loss of information in list->vector and friends; you lose eq-ness and location identity. (vector->list (list->vector l)) gives a copy of the list, so rplacas and rplacds will affect different cells. A symbol is more than just an alternate representation for a string. It can be an identifier or keyword. Some implementations allow symbols to have property lists. All of this information is lost in the conversion from symbol to string, some is regained in the opposite conversion. I tend to think of the name of a symbol as an attribute of that symbol. "symbol->string" is more correctly called "symbol-name". How do symbols differ from numbers, which can also be identifiers or "keywords," in this regard? The way I look at things, all associations of information with symbols are through tables not intimately tied to the symbol itself. To say that symbols "have" values is like saying that integers "have" values - getting a value out of an environment, given a symbol, is completely analogous to getting a value out of a vector, given an integer. "Essential scheme" intentionally doesn't have property lists. Symbol eq-ness is global, so property lists would violate the desire (based on modularity considerations) to avoid global state; and given reasonable association tables, they are unnecessary. Furthermore, whereas one can expect "string->list" to return a new list made up of unique cons-cells, "string->symbol" may return an existing symbol. The name "intern" is more appropriate for this functionality. (Perhaps "hash-make-symbol" would be a better name.) But character->integer (or char->ascii or whatever you want to call it) may return an existing integer. What is the difference? Since symbols are immutable objects, like numbers, it's okay that an existing one is returned. Reference to hashing doesn't seem very abstract to me... If making a unique, uninterned symbol is supported, the name "make-symbol" is appropriate. I personally don't believe in uninterned symbols (yet?), but if they existed then make-symbol would be an okay name, maybe better than string->uninterned-symbol, but I don't much care. I think that the "->" convention shouldn't have such a terribly strict meaning. I guess it's my feeling that string->symbol and symbol->string are acceptable even if they aren't strictly analogous to list->vector and vector->list. Jonathan Rees (JAR@MC)  Received: from unc by csnet-relay.csnet id aa11924; 4 Feb 85 5:13 EST Received: by unc (4.12/4.7) id AA00752; Mon, 4 Feb 85 00:29:49 est Date: Mon, 4 Feb 85 00:29:49 est From: Kent Dybvig Message-Id: <8502040529.AA00752@unc> To: Scheme@mit-mc.ARPA Subject: Chez Scheme => Chez Scheme Chez Scheme is an implementation of Scheme for Vaxes running 4.2 Bsd Unix. Chez Scheme supports all required and most optional features of the anticipated Scheme standard. The first Chez Scheme release will include an extensive reference manual. A Chez Scheme tutorial is in preparation for later releases. Features of Scheme: o Clean, concise dialect of Lisp o Lexically scoped (as is Common Lisp) o Full function closures (first-class, full funarg) o Tail-recursion reliably translated into iteration o Full upward/downward continuations Features of Chez Scheme: o Incremental native-code (Vax object code) compiler o Flexible user interface o Fast-loading compiled files o Very fast arbitrary precision integer and rational arithmetic o Programmable exception handlers o Support for multi-tasking (timer interrupts, continuations) o String and vector operations o Macros and structures o Engines (a process abstraction) Application programs distributed with the first release of Chez Scheme include a set operation package, a logic programming subsystem, a lazy-cons facility, and a generic matrix, vector and scalar multiplication package. Faster than many Lisp systems, Chez Scheme may be the fastest Scheme available. On the Vax 11/780, Chez Scheme is competitive with benchmarks reported for Franz Lisp and Digital Common Lisp at last summer's AAAI conference in Austin, TX. For example, Chez Scheme runs the "Tak" benchmark in 3.4 cpu seconds and the "Deriv" benchmark in 21.9 cpu seconds. The code tested contained no declarations, used generic arithmetic, and had no inlined calls. No separate compilation phase is necessary: all code loaded into Chez Scheme is compiled incrementally. Chez Scheme is available for mid-March distribution to US educational institutions only. We will send a license agreement to interested parties. There is a $400 distribution fee. We are not yet able to do foreign or commercial distributions, but contact us if you are interested. Write for a copy of the license agreement and ordering information to: R. Kent Dybvig Department of Computer Science New West Hall (035-A) University of North Carolina Chapel Hill, NC 27514 USA decvax!mcnc!unc!dyb (usenet) dyb.unc@Csnet-Relay (ARPANET)  Received: from unc by csnet-relay.csnet id ab08903; 3 Feb 85 13:47 EST Received: by unc (4.12/4.7) id AA06730; Sun, 3 Feb 85 12:00:27 est Date: Sun, 3 Feb 85 12:00:27 est From: Kent Dybvig Message-Id: <8502031700.AA06730@unc> To: scheme@mit-mc.ARPA Subject: string->symbol, symbol->string "symbol->string" and "string->symbol functions are not analogous to "list->string" or "string->list". "list->string" converts the same data from one representation into another, with no loss of information. The same is true for "string->list", "list->vector", "vector->list" and "character->integer". A symbol is more than just an alternate representation for a string. It can be an identifier or keyword. Some implementations allow symbols to have property lists. All of this information is lost in the conversion from symbol to string, some is regained in the opposite conversion. I tend to think of the name of a symbol as an attribute of that symbol. "symbol->string" is more correctly called "symbol-name". Furthermore, whereas one can expect "string->list" to return a new list made up of unique cons-cells, "string->symbol" may return an existing symbol. The name "intern" is more appropriate for this functionality. (Perhaps "hash-make-symbol" would be a better name.) If making a unique, uninterned symbol is supported, the name "make-symbol" is appropriate. If what we are really doing when we say "make-symbol" or "string->symbol" was changing representations, then "string->symbol" would be fine. But it isn't a type conversion we are doing, it's symbol creation (or lookup). It seems analogous to saying "make-vector" should be called "integer->vector" because we are taking an integer, n, and returning a vector of length n. The name is only one aspect of a symbol in much the same way that the length is only one aspect of a vector. Kent Dybvig decvax!mcnc!unc!dyb dyb.unc@Csnet-Relay  Received: from unc by csnet-relay.csnet id aa08903; 3 Feb 85 13:47 EST Received: by unc (4.12/4.7) id AA06699; Sun, 3 Feb 85 11:59:27 est Date: Sun, 3 Feb 85 11:59:27 est From: Kent Dybvig Message-Id: <8502031659.AA06699@unc> To: scheme@mit-mc.ARPA Subject: Re: Scheme String Operations 1. I prefer Chris Hanson's names over the Common Lisp or Bartley names. I find the CL names confusing and nonsensical. Why does "char=" imply case sensitivity, and "char-equal" insensitivity? The issue is also confused because CL does not define "equal" to use "char-equal" as is implied by the naming convention. I have been using Common Lisp for over a year since helping to bring Data General's system into existence. Many of us get confused about which is which. There would be no confusion with a suffix such as "-ci". Except for possible commercial interests, I can see no benefit in copying the mistakes of Common Lisp. 2. I would rather spell out the name "character" in all functions rather than use the shorter "char" but that's probably too much to ask. 3. I wish there weren't so many string functions. I think Chris Hanson was on the right track with the very primitive set that can be used to construct all the others. Perhaps we should have that set plus "string-ref", "string-equal?", "string-less?" "string-equal-ci?", and "string-less-ci?", "string->list", and "list->string". 4. I have a "string" function in my system that comes in handy -- (string char1 char2 ... charN) => string of char1 ... charN, N >= 0. For example: (string #\a #\b #\c) => "abc". This is easy to write as (lambda x (list->string x)). It is particularly useful when creating a cursor-addressing string to send to the tty. 5. I do not view "string->symbol" and "symbol->string" as string functions. They are symbol creation and destructuring primitives. In fact, I strongly object to the names since they are not analogous to other type-conversion functions such as "string->list" and "character->integer". I'll say more about this in a separate note. 6. As far as optionality is concerned, I would like to see Scheme adopt an optional argument mechanism. It reduces the namespace and is really quite easy to implement efficiently. A function like "member" becomes completely general, allowing not only "eq?", "eqv?" and "equal?" tests but also any user-defined test. However, until we have optional arguments that can be defined using a lambda expression, I do not think we should have primitives with optional arguments. Either the language has them or it doesn't. Kent Dybvig decvax!mcnc!unc!dyb dyb.unc@Csnet-Relay  Received: from ti-csl by csnet-relay.csnet id ah03968; 2 Feb 85 10:36 EST Date: 1 Feb 1985 1453-CST From: David Bartley Subject: Scheme String Operations To: Scheme@mit-mc.ARPA, CPH%MIT-OZ@mit-mc.ARPA cc: SCHEME.Users%ti-csl.csnet@csnet-relay.arpa, Bartley%ti-csl.csnet@csnet-relay.arpa Received: from csl60 by ti-csl; Fri, 1 Feb 85 16:57 CST We at Texas Instruments propose the following revisions to Chris Hanson's preliminary report on Scheme string operations. Our implementation has already begun, so we would like to hear objections and other comments as soon as possible. Thanks! 1. Name changes For reasonable consistency with Common LISP, we propose the following name changes: Preliminary report Our proposal Common LISP ================== ============ =========== char-equal? char= char= char-equal-ci? char-equal? char-equal char-less? char< char< char-less-ci? char-less? char-lessp substring-equal? substring= string-equal? string= substring-equal-ci? substring-equal? string-equal-ci? string-equal? substring-less? substring< string-less? string< substring-less-ci? substring-less? string-less-ci? string-less? 2. Added features (CHAR? obj) is needed for argument checking, if nothing else. We agreed at the workshop to include SYMBOL->STRING and STRING->SYMBOL as essential and STRING->UNINTERNED-SYMBOL as optional. 3. Changes CHAR-SET-MEMBER should take its arguments in the same order as MEMBER. 4. Essential features We are not sure how to separate ``essential'' from ``optional'' features, but will probably go with everything labeled ``minimal'' below. Those labeled ``elective'' will be available but not part of the initial load of the system. Basic Character Operations minimal: CHAR?, CHAR=, CHAR-EQUAL?, CHAR<, CHAR-LESS?, CHAR-UPCASE, CHAR-DOWNCASE elective: CHAR-UPPER-CASE?, CHAR-LOWER-CASE?, CHAR-SET-MEMBER? Basic String Operations minimal: STRING?, STRING-LENGTH, STRING-REF, STRING-SET!, STRING->SYMBOL, STRING->UNINTERNED-SYMBOL, SYMBOL->STRING elective: STRING-ALLOCATE Standard Operations minimal: MAKE-STRING, STRING-FILL!, STRING-NULL?, SUBSTRING, STRING-APPEND, STRING-COPY, STRING->LIST, LIST->STRING Motion Primitives minimal: SUBSTRING-FILL!, SUBSTRING-MOVE-RIGHT!, SUBSTRING-MOVE-LEFT! Comparison Primitives minimal: SUBSTRING=, STRING=, SUBSTRING-EQUAL?, STRING-EQUAL?, SUBSTRING<, STRING<, SUBSTRING-LESS?, STRING-LESS? elective: SUBSTRING-MATCH-FORWARD, STRING-MATCH-FORWARD, SUBSTRING-MATCH-FORWARD-CI, STRING-MATCH-FORWARD-CI, SUBSTRING-MATCH-BACKWARD, STRING-MATCH-BACKWARD, SUBSTRING-MATCH-BACKWARD-CI, STRING-MATCH-BACKWARD-CI Character Search Primitives elective: all Case elective: all -- Regards, David Bartley -------  Received: from indiana by csnet-relay.csnet id aa04954; 1 Feb 85 12:23 EST Date: Fri, 1 Feb 85 11:13:53 est From: Will Clinger Received: by iuvax.UUCP; id AA02926; Fri, 1 Feb 85 11:13:53 est To: scheme@mit-mc.ARPA Subject: quotient, remainder, letrec In the preliminary report on the Brandeis workshop, I said that quotient and remainder were such that if x, y, q, and r are integers such that x = qy + r, y is nonzero, r has the same sign as y and has absolute value less than that of y, then (quotient x y) was q and (remainder x y) was r. Since that conflicts with Common Lisp, Franz Lisp, T, Scheme 84, Scheme 312, and maybe MIT Scheme (my documentation on MIT Scheme isn't clear on the issue), I retract that definition in favor of: If x, y, q, and r are integers such that x = qy + r, y is nonzero, r has the same sign as x, and |r| < |y|, then (quotient x y) is q and (remainder x y) is r. The definition of letrec in the preliminary report is wrong. William Clinger willc@indiana  Received: from brandeis by csnet-relay.csnet id a002562; 31 Jan 85 12:13 EST Received: by brandeis.ARPA (4.12/4.7) id AA14346; Thu, 31 Jan 85 10:14:16 est Date: 31 Jan 1985 09:53-EST From: mw%brandeis.csnet@csnet-relay.arpa In-Real-Life: Mitchell Wand,faculty Subject: Resources for Scheme course To: scheme@mit-mc.ARPA, cth%indiana.csnet@csnet-relay.arpa Cc: jc%brandeis.csnet@csnet-relay.arpa Message-Id: <476031183/mw@brandeis> Brandeis is currently considering creating a laboratory facility for a course on the style of the Abelson & Sussman book. For this purpose, I am collecting data on the state of various workstation implementations of Scheme and related languages (e.g. T). I know about some implementations, but I'd like to hear about others, and get up-to-date information even about the ones I've heard of. In particular: 1) What workstations would you recommend for such an undertaking, and what is the availability of appropriate software? 2) What ratio of students/workstation would you recommend? To give some idea of the planned intensity of the course, let's assume that we plan to cover about half of the material in A&S in a semester. 3) How many square feet per workstation should we plan on? What other things in the physical arrangment of the facility should we watch out for? Reply to mw@brandeis on csnet. I will post a summary of replies to the mailing list if interest warrants. Thanks for your help. -- Mitch Wand  Received: from indiana by csnet-relay.csnet id aa09096; 24 Jan 85 14:38 EST Date: Thu, 24 Jan 85 13:27:13 est From: Will Clinger Received: by iuvax.UUCP; id AA11826; Thu, 24 Jan 85 13:27:13 est To: scheme@mit-mc.ARPA Subject: Purpose of a "common" Scheme Surely different people have different purposes in wanting a more standardized Scheme. Half the fun is getting everyone to work together and to agree. It helps to be vague about the purposes. Here's my view, from a draft introduction to the final report on the workshop: Scheme shares with Common Lisp [\ref] the goal of a core language common to several implementations. Scheme differs from Common Lisp in that the purpose of the common language has more to do with porting ideas than with porting code. It is appropriate therefore that Scheme is much smaller, is less pervasively specified, and will evolve faster than Common Lisp. Let me know by direct mail if you object to that wording. William Clinger (willc@indiana)  Date: Wed, 16 Jan 1985 02:40 EST Message-ID: From: CPH%MIT-OZ@MIT-MC.ARPA To: David Bartley Cc: Jensen%ti-csl.csnet@CSNET-RELAY.ARPA, Oxley%ti-csl.csnet@CSNET-RELAY.ARPA, Scheme@MIT-MC.ARPA Subject: Scheme String Operations: the Report In-reply-to: Msg of 15 Jan 1985 15:45-EST from David Bartley Date: Tuesday, 15 January 1985 15:45-EST From: David Bartley -- We probably need a (CHAR? obj) predicate. The following are useful; can we agree on their meaning? (CHAR->INTEGER char) ; CL's CHAR-CODE ? (INTEGER->CHAR n) ; CL's CODE-CHAR ? -- How is a CHAR-SET represented? Created? Modified? -- Is the character datatype in MIT Scheme extended the same way CL has gone? Do you support font and/or bit info? Have you added operations to MIT Scheme beyond those you've proposed in your report? In answer to all of these: I purposefully limited my description of characters (and character sets) to that minimum required to describe the string abstraction. I did this because I felt that the string abstraction was very largely independent of the character abstraction; this method shows the dependence pretty clearly. Further, I recall that we agreed to do characters "the CL way" at the workshop, at least so far as syntax. My character datatype is heavily influenced by CL, and implements bits but not fonts (I don't need them now). I would be willing to discuss both this and the character set abstraction if there is interest. -- You were clearly influenced by Common LISP (CL)... Not so! The string operations were mostly designed from scratch; I was unaware that they were so similar. ...CL uses the suffixes =, <, etc. for case sensitive comparisons of characters and strings and the suffixes EQUAL, LESSP, etc for case-insensitive comparisons. I prefer to adopt that convention... Mumble. I don't particularly like those names, but then, I don't particularly care about the names anyway. I'm not terribly happy with the names I chose, either. Whatever folks like is fine with me. -- May I assume that string and character values are printed (and read) as defined by CL? Yes; I think that we agreed upon this at the workshop. -- May I assume that all of the operations you defined are procedures, not special forms? Yes!!! (with feeling) -- May I assume that SUBSTRING and other names written without ! always return a copy rather than sharing structure? Yes. Furthermore, operations written with a "!" return undefined values. -- Why distinguish STRING-ALLOCATE from MAKE-STRING? By analogy with MAKE-VECTOR, shouldn't MAKE-STRING take the second argument optionally? -- One way to reduce the number of operations is to combine the STRING- and SUBSTRING- operations by accepting optional substring operands. Gee, I guess that I don't feel too good about that. While it is true that this cuts down on the number of names, I have an irrational fear of widespread optionalogy. Perhaps it is from overexposure to flagrant misuse, but I prefer not to use optionals at the "lower levels" of my code. Also, I was particularly thinking about the workshop, and I recall there was very mixed feeling about optional arguments, rest arguments, etc... I felt that this would be more acceptable to the community as a whole. -- The order of arguments to CHAR-SET-MEMBER? is reversed from that of MEMBER, MEMV, and MEMQ. Hmm... This was chosen to match other stuff... in particular, VECTOR-REF, LIST-REF, etc. The convention is: the compound structure first, the key second. In hindsight, it clearly should match MEMfoo. -- What should happen when indexes cross (start>end) or go outside the proper range? Sorry, I should have spelled that out. It will signal an error, in all cases (although maybe not at the most reasonable place, in my implementation). I specific: everywhere where I have described the arguments as "a string", "a character", "a substring", etc., those can be construed as requirements. Errors will happen if those things are not true. -- Are you suggesting that the Basic Character Operations, Basic String Operations, and Standard Operations be 'required' and the rest be 'optional'? If not, where would you draw the line? Do you use all of these operations yourself? No, I was making no such suggestion; I would be hesitant to do so without some discussion. I do feel that the 'Basic String Operations' and 'Standard Operations', with the exception of STRING-ALLOCATE, STRING-SET!, and STRING-FILL!, are very useful, and perhaps should be required. But then we have already agreed upon those in the 'Basic' category, and I think that there would be little disagreement about the 'Standard' ones. But I can't really say where one should draw the line; all of these procedures are useful if one ever does anything even moderately hairy. Personally, I have used most of these operations pretty freely in the editor. I have found, though, that a particular pattern has emerged. The mutating operations are used (in conjunction with STRING-ALLOCATE) almost exclusively for the construction of higher level, non-mutating operations like SUBSTRING and STRING-APPEND. I think that such things might safely be relegated to the realm of 'system programming', for those who prefer to make such a distinction. Anyway, if there is interest in working out the boundaries of 'required' vs. 'optional' here, I am perfectly willing to add more of my flame to the conflagration.  Received: from ti-csl by csnet-relay.csnet id ab07537; 15 Jan 85 22:01 EST Date: 15 Jan 1985 1445-CST From: David Bartley Subject: Re: Scheme String Operations: the Report To: CPH%MIT-OZ@mit-mc.ARPA, Bartley%ti-csl.csnet@csnet-relay.arpa, Jensen%ti-csl.csnet@csnet-relay.arpa, Oxley%ti-csl.csnet@csnet-relay.arpa, Scheme@mit-mc.ARPA cc: Bartley%ti-csl.csnet@csnet-relay.arpa In-Reply-To: Your message of 14-Jan-85 0529-CST Received: from csl60 by ti-csl; Tue, 15 Jan 85 20:45 CST Chris, Thanks for mailing out the preliminary report on your proposal for string operations for Scheme. Here are my initial reactions. -- You were clearly influenced by Common LISP (CL), since at least 27 of your functions have direct analogues in CL. You rarely use the same name as CL uses, however. I tend to agree with most of your name choices, with one exception: CL uses the suffixes =, <, etc. for case sensitive comparisons of characters and strings and the suffixes EQUAL, LESSP, etc for case-insensitive comparisons. I prefer to adopt that convention rather than inventing the suffix -CI. We should definitely preserve our conventions regarding suffixed ? and ! characters. Thus, CL's CHAR-LESSP and NSTRING-UPCASE should be renamed CHAR-LESS? and STRING-UPCASE! for Scheme. -- We probably need a (CHAR? obj) predicate. The following are useful; can we agree on their meaning? (CHAR->INTEGER char) ; CL's CHAR-CODE ? (INTEGER->CHAR n) ; CL's CODE-CHAR ? -- The order of arguments to CHAR-SET-MEMBER? is reversed from that of MEMBER, MEMV, and MEMQ. -- How is a CHAR-SET represented? Created? Modified? -- Why distinguish STRING-ALLOCATE from MAKE-STRING? By analogy with MAKE-VECTOR, shouldn't MAKE-STRING take the second argument optionally? -- May I assume that string and character values are printed (and read) as defined by CL? -- May I assume that all of the operations you defined are procedures, not special forms? -- May I assume that SUBSTRING and other names written without ! always return a copy rather than sharing structure? -- One way to reduce the number of operations is to combine the STRING- and SUBSTRING- operations by accepting optional substring operands. For example, specify only (STRING-FILL! string char { start { end }} ) instead of (STRING-FILL! string char) and and (SUBSTRING-FILL! string start end char) Note that the argument has been repositioned. It would be useful to let either or default. In the example above, we could interpret (STRING-FILL! string char start) as filling from to the end of the string. -- What should happen when indexes cross (start>end) or go outside the proper range? -- Are you suggesting that the Basic Character Operations, Basic String Operations, and Standard Operations be 'required' and the rest be 'optional'? If not, where would you draw the line? Do you use all of these operations yourself? -- Is the character datatype in MIT Scheme extended the same way CL has gone? Do you support font and/or bit info? Have you added operations to MIT Scheme beyond those you've proposed in your report? -- In summary, the proposal looks sound and I have no arguments with the functionality you are proposing. I'll pass on further comments after we've had time to digest it more thoroughly. Regards, David Bartley -------  Received: from unc by csnet-relay.csnet id a000234; 14 Jan 85 5:09 EST Received: by unc (4.12/4.7) id AA00749; Sun, 13 Jan 85 22:21:36 est Date: Sun, 13 Jan 85 22:21:36 est From: Kent Dybvig Message-Id: <8501140321.AA00749@unc> To: KMP@mit-mc.ARPA, SCHEME@mit-mc.ARPA Subject: Re: policy to adopt I have certainly gotten the impression that everyone WAS after a Common Scheme of sorts, and not just so that papers use common syntax. Why standardize on so many of the function names? More importantly, why standardize on the controversial nil/t/boolean issue? Why have numbers and streams subcommittees? I think we need more discussion of the details of the proposal rather than less. Standardizing is dangerous if done only half- heartedly. We must be absolutely sure of what we standardize on, since we will likely be stuck with our decisions for several years. As for the list-length, list-append issue, I think we ought to take one of two stances: Stance 1: sequence-type functions such as ref, length and append should be prefixed by "list-" for the list version, "string-" for the string version, and "vector-" for the vector version. Stance 2: sequence-type functions such as ref, length and append should be prefixed by nothing for the list version, "string-" for the string version, and "vector-" for the vector version. The first stance is preferable because of the symmetry, the second because the names for the list versions (probably the most commonly used) are shorter. I think that the first stance is less confusing and less likely to cause trouble. Cheers, Kent Dybvig  Date: Mon, 14 Jan 1985 00:17 EST Message-ID: From: CPH%MIT-OZ@MIT-MC.ARPA To: David Bartley , Jensen%ti-csl.csnet@CSNET-RELAY.ARPA, Oxley%ti-csl.csnet@CSNET-RELAY.ARPA, Scheme@MIT-MC Subject: Scheme String Operations: the Report In-reply-to: Msg of 13 Jan 1985 15:14-EST from CPH Here is the preliminary report... Comments and suggestions? ---------------------------------------------------------------------- Scheme String Operations Notes: [] Strings are mutable vectors of characters. [] The string datatype described here has very little dependence on the character datatype from which it is built. Thus it may correspond to either of Common Lisp's STRING or SIMPLE-STRING datatypes. In MIT Scheme, the character datatype is a special extended ASCII. [] Historically, two different methods have been used to specify substrings: [1] a starting index and a length, and [2] a starting index and an ending index. Often both methods have been used for different operations in the same data abstraction. The arguments in favor of one method or the other seem fairly uninteresting, but it does seem important to pick one method and use it exclusively. The latter method has been chosen, for no particularly good reason. Naming conventions: [] Character-wise operations are normally specified in two forms, one which operates on a substring, and another which operates on an entire string. The former is usually named "SUBSTRING-..." and the latter "STRING-...". [] Operations for which case sensitivity has meaning are specified in two forms, a case-sensitive version and a case-insensitive version. The latter is given the suffix "-CI" to distinguish it; if the operation is a predicate, the suffix precedes the "?" at the end of the name. [] Currently, operation pairs which are "directed" will be given similar names, such as, "STRING-FIND-NEXT-CHAR" and "STRING-FIND-PREVIOUS-CHAR". However, there are 3 different syllable pairs used to distinguish these operations: "LEFT"/"RIGHT", "PREVIOUS"/"NEXT", and "FORWARD"/"BACKWARD". In each case the particular pair was chosen for its meaning, but it may be better to name them more consistently. ---------------------------------------------------------------------- Definitions [] The phrases "X is a string" and "the string X" mean that X is an object which satisfies the STRING? predicate. Similarly, "X is a character" means that X satisfies the CHAR? predicate. [] The "length" of a string is the number of characters that it contains. This number is a non-negative integer that is determined at the time of the string's creation. [] The phrase "X is a valid index of Y", where Y is a string, means that X is a non-negative integer that is strictly less than the length of Y. The index 0 refers to the first character, 1 to the second, etc. [] The phrase " is a substring" means that: 1. X is a string. 2. Both Y and Z are non-negative integers. 3. Z is less than or equal to the length of X. 4. Y is less than or equal to Z. This refers to that substring of X starting at Y (inclusive) and ending at Z (exclusive). If Y is equal to Z, the substring is empty. [] The directions "left" and "right" refer to numerically decreasing and numerically increasing indices, respectively. Alternate names for these directions are (respectively): "previous" and "next"; "backward" and "forward". ---------------------------------------------------------------------- Basic Character Operations These are the operations on characters that are required by the string data abstraction. The names of these operations are irrelevant; only the functionality is required. (CHAR-EQUAL? CHAR1 CHAR2) (CHAR-EQUAL-CI? CHAR1 CHAR2) These predicates are true iff CHAR1 and CHAR2 are the same character. The former operation is case sensitive, while the latter is not. Both CHAR1 and CHAR2 must be characters. (CHAR-LESS? CHAR1 CHAR2) (CHAR-LESS-CI? CHAR1 CHAR2) These predicates are true iff CHAR1 is strictly less than CHAR2 in the character ordering. The former operation is case sensitive, while the latter is not. Both CHAR1 and CHAR2 must be characters. (CHAR-UPPER-CASE? CHAR) (CHAR-LOWER-CASE? CHAR) These predicates should be true only of upper and lower case alphabetic characters, respectively. CHAR must be a character. (CHAR-UPCASE CHAR) (CHAR-DOWNCASE CHAR) These operations convert alphabetic characters to upper and lower case, respectively. If CHAR is not alphabetic, it is returned. CHAR must be a character. Character-sets A character-set is an abstract object that represents a (mathematical) set of characters. Character-set searches are most useful for parsing strings. The required operations on character-sets are: (CHAR-SET-MEMBER? CHAR-SET CHAR) A predicate which is true iff the character CHAR is a member of the character-set CHAR-SET. ---------------------------------------------------------------------- Basic String Operations These are the most primitive of the string operations. All other string operations can be constructed from these. (STRING-ALLOCATE LENGTH) This operation allocates a new string, of the given LENGTH, and returns it. The contents of the string are unspecified. LENGTH must be a non-negative integer. (STRING? OBJECT) This is a boolean operation which is true of all strings. (STRING-LENGTH STRING) This operation returns the length of STRING, which must be a string. The value is a non-negative integer. (STRING-REF STRING INDEX) This operation returns the INDEX'th element of STRING, a character. STRING must be a string, and INDEX must be a valid index of STRING. (STRING-SET! STRING INDEX CHAR) This operation stores the character CHAR as the INDEX'th element of STRING. STRING must be a string, INDEX must be a valid index of STRING, and CHAR must be a character. ---------------------------------------------------------------------- Standard Operations These operations are useful enough to deserve being "standard" in most implementations. (MAKE-STRING LENGTH CHAR) This allocates a new string of the given LENGTH, and initializes all of its elements to CHAR. LENGTH must be a non-negative integer, and CHAR must be a character. (STRING-FILL! STRING CHAR) This fills the string STRING with the character CHAR. (STRING-NULL? STRING) This predicate is true only of strings whose length is zero. STRING must be a string. (SUBSTRING STRING START END) This operation returns a new string, which is the substring designated by ; this must be a valid substring designator. (STRING-APPEND STRING1 STRING2) This operation returns a new string, which is the concatenation of STRING1 followed by STRING2, both of which must be strings. This operation may (optionally) be n-ary, for n greater than 0. (STRING-COPY STRING) This operation returns a new copy of STRING, which must be a string. (STRING->LIST STRING) (LIST->STRING CHARS) These operations convert between strings and lists of characters. STRING must be a string, and CHARS must be a list of characters. The result of either operation is a newly-created object of the appropriate form. ---------------------------------------------------------------------- Motion Primitives These operations are useful because they can be used to construct many other string operations, e.g. SUBSTRING and STRING-APPEND. If strings are implemented in the usual way on conventional machines, these operations are extremely simple to implement. It is also possible to in-line code them in some cases. (SUBSTRING-FILL! STRING START END CHAR) This fills the substring with the character CHAR. (SUBSTRING-MOVE-RIGHT! STRING1 START1 END1 STRING2 START2) (SUBSTRING-MOVE-LEFT! STRING1 START1 END1 STRING2 START2) These operations destructively copy the substring to the string STRING2 starting at the index START2. It must be the case that is a substring; this latter substring is destructively modified to contain the contents of the former substring. The operations differ only when the two substrings overlap, i.e. when STRING1 and STRING2 are EQ? and the index sets of the substrings are not disjoint. In this case, the operations are defined to copy the elements of the first substring serially. SUBSTRING-MOVE-RIGHT! copies the first substring from left to right, while SUBSTRING-MOVE-LEFT! copies from right to left. Thus, for example, the two operations can be used to shift groups of characters right or left, respectively, within a given string. ---------------------------------------------------------------------- Comparison Primitives Each group of comparisons contains these operations: 1. Compare two substrings, case sensitive. 2. Compare two strings, case sensitive. 3. Compare two substrings, case insensitive. 4. Compare two strings, case insensitive. The groups are described as a unit since they are essentially very similar. In all cases the arguments must be valid substrings or strings. (SUBSTRING-EQUAL? STRING1 START1 END1 STRING2 START2 END2) (STRING-EQUAL? STRING1 STRING2) (SUBSTRING-EQUAL-CI? STRING1 START1 END1 STRING2 START2 END2) (STRING-EQUAL-CI? STRING1 STRING2) These are boolean equality predicates, which are true only when the two (sub)strings are the same length and contain the same characters. (SUBSTRING-LESS? STRING1 START1 END1 STRING2 START2 END2) (STRING-LESS? STRING1 STRING2) (SUBSTRING-LESS-CI? STRING1 START1 END1 STRING2 START2 END2) (STRING-LESS-CI? STRING1 STRING2) These are boolean order predicates, which compare the (sub)strings using the standard dictionary order; i.e. the leftmost unequal characters determine the order, or if no such character exists, the shorter (sub)string is less. The character ordering is determined by the character operations CHAR-LESS? and CHAR-LESS-CI?. (SUBSTRING-MATCH-FORWARD STRING1 START1 END1 STRING2 START2 END2) (STRING-MATCH-FORWARD STRING1 STRING2) (SUBSTRING-MATCH-FORWARD-CI STRING1 START1 END1 STRING2 START2 END2) (STRING-MATCH-FORWARD-CI STRING1 STRING2) These operations compare the (sub)strings from left to right, returning the number of characters that were the same. (SUBSTRING-MATCH-BACKWARD STRING1 START1 END1 STRING2 START2 END2) (STRING-MATCH-BACKWARD STRING1 STRING2) (SUBSTRING-MATCH-BACKWARD-CI STRING1 START1 END1 STRING2 START2 END2) (STRING-MATCH-BACKWARD-CI STRING1 STRING2) These operations compare the (sub)strings from right to left, returning the number of characters that were the same. ---------------------------------------------------------------------- Character Search Primitives Each group of searches contains these operations: 1. Search substring for character, case sensitive. 2. Search string for character, case sensitive. 3. Search substring for character, case insensitive. 4. Search string for character, case insensitive. 5. Search substring for character in set. 6. Search string for character in set. In all cases the arguments must be valid substrings, strings, characters, or character-sets, as appropriate. (SUBSTRING-FIND-NEXT-CHAR STRING START END CHAR) (STRING-FIND-NEXT-CHAR STRING CHAR) (SUBSTRING-FIND-NEXT-CHAR-CI STRING START END CHAR) (STRING-FIND-NEXT-CHAR-CI STRING CHAR) (SUBSTRING-FIND-NEXT-CHAR-IN-SET STRING START END CHAR-SET) (STRING-FIND-NEXT-CHAR-IN-SET STRING CHAR-SET) These operations return the index of the leftmost occurrence of the character CHAR (or character-set CHAR-SET) within the given (sub)string, or #!FALSE if there is no occurrence. (SUBSTRING-FIND-PREVIOUS-CHAR STRING START END CHAR) (STRING-FIND-PREVIOUS-CHAR STRING CHAR) (SUBSTRING-FIND-PREVIOUS-CHAR-CI STRING START END CHAR) (STRING-FIND-PREVIOUS-CHAR-CI STRING CHAR) (SUBSTRING-FIND-PREVIOUS-CHAR-IN-SET STRING START END CHAR-SET) (STRING-FIND-PREVIOUS-CHAR-IN-SET STRING CHAR-SET) These operations return the index of the rightmost occurrence of the character CHAR (or character-set CHAR-SET) within the given (sub)string, or #!FALSE if there is no occurrence. ---------------------------------------------------------------------- Case Each of the following groups contains the following operations: 1. A predicate to determine a substring's case. 2. A predicate to determine a string's case. 3. A operation to destructively set a substring's case. 4. A operation to destructively set a string's case. The meanings of the operations should be clear from their names. (SUBSTRING-UPPER-CASE? STRING START END) (STRING-UPPER-CASE? STRING) (SUBSTRING-UPCASE! STRING START END) (STRING-UPCASE! STRING) (SUBSTRING-LOWER-CASE? STRING START END) (STRING-LOWER-CASE? STRING) (SUBSTRING-DOWNCASE! STRING START END) (STRING-DOWNCASE! STRING) (SUBSTRING-CAPITALIZED? STRING START END) (STRING-CAPITALIZED? STRING) (SUBSTRING-CAPITALIZE! STRING START END) (STRING-CAPITALIZE! STRING) The (sub)string in the ...CAPITALIZE... operations may not be null. ---------------------------------------------------------------------- Appendix: An Implementation The following is an examplary implementation of the above string operations (in terms of the basic operations). This implementation is intended to supplement the preceding specification by providing an explicit formal description of the semantics of the operations. There is no error checking in this code, since it was felt that it would obscure the form somewhat. ;;;; Standard Operations (define (make-string length char) (let ((result (string-allocate length))) (substring-fill! result 0 length char) result)) (define (string-fill! string char) (substring-fill! string 0 (string-length string) char)) (define (string-null? string) (zero? (string-length string))) (define (substring string start end) (let ((result (string-allocate (- end start)))) (substring-move-right! string start end result 0) result)) (define (string-append string1 string2) (let ((length1 (string-length string1)) (length2 (string-length string2))) (let ((result (string-allocate (+ length1 length2)))) (substring-move-right! string1 0 length1 result 0) (substring-move-right! string2 0 length2 result length1) result))) (define (string-copy string) (let ((length (string-length string))) (let ((result (string-allocate length))) (substring-move-right! string 0 length result 0) result))) (define (string->list string) (let ((end (string-length string))) (define (loop index) (if (= index end) '() (cons (string-ref string index) (loop (1+ index))))) (loop 0))) (define (list->string chars) (let ((result (string-allocate (length chars)))) (define (loop index chars) (if (null? chars) result (begin (string-set! result index (car chars)) (loop (1+ index) (cdr chars))))) (loop 0 chars))) ;;;; Motion Primitives (define (substring-fill! string start end char) (define (loop index) (if (not (= index end)) (begin (string-set! string index char) (loop (1+ index))))) (loop start)) (define (substring-move-right! string1 start1 end1 string2 start2) (define (loop index1 index2) (if (not (= index1 end1)) (begin (string-set! string2 index2 (string-ref string1 index1)) (loop (1+ index1) (1+ index2))))) (loop start1 start2)) (define (substring-move-left! string1 start1 end1 string2 start2) (define (loop index1 index2) (if (not (= index1 start1)) (begin (string-set! string2 (-1+ index2) (string-ref string1 (-1+ index1))) (loop (-1+ index1) (-1+ index2))))) (loop end1 end2)) ;;;; Comparison Primitives (define substring-equal?) (define substring-equal-ci?) (let () (define (make-substring-equal? char-equal?) (lambda (string1 start1 end1 string2 start2 end2) (define (loop index1 index2) (or (= index1 end1) (and (char-equal? (string-ref string1 index1) (string-ref string2 index2)) (loop (1+ index1) (1+ index2))))) (and (= (- end1 start1) (- end2 start2)) (loop start1 start2)))) (set! substring-equal? (make-substring-equal? char-equal?)) (set! substring-equal-ci? (make-substring-equal? char-equal-ci?))) (define substring-less?) (define substring-less-ci?) (let () (define (make-substring-less? char-equal? char-less?) (lambda (string1 start1 end1 string2 start2 end2) (define (loop index1 index2) (cond ((or (= index1 end1) (= index2 end2)) (< (- end1 start1) (- end2 start2))) ((char-equal? (string-ref string1 index1) (string-ref string2 index2)) (loop (1+ index1) (1+ index2))) (else (char-less? (string-ref string1 index1) (string-ref string2 index2))))) (loop start1 start2))) (set! substring-less? (make-substring-less? char-equal? char-less?)) (set! substring-less-ci? (make-substring-less? char-equal-ci? char-less-ci?))) (define substring-match-forward) (define substring-match-forward-ci) (let () (define (make-substring-match-forward char-equal?) (lambda (string1 start1 end1 string2 start2 end2) (define (loop index1 index2 n) (if (or (= index1 end1) (= index2 end2) (not (char-equal? (string-ref string1 index1) (string-ref string2 index2)))) n (loop (1+ index2) (1+ index2) (1+ n)))) (loop start1 start2 0))) (set! substring-match-forward (make-substring-match-forward char-equal?)) (set! substring-match-forward-ci (make-substring-match-forward char-equal-ci?))) (define substring-match-backward) (define substring-match-backward-ci) (let () (define (make-substring-match-backward char-equal?) (lambda (string1 start1 end1 string2 start2 end2) (define (loop index1 index2 n) (if (or (= index1 start1) (= index2 start2) (not (char-equal? (string-ref string1 (-1+ index1)) (string-ref string2 (-1+ index2))))) n (loop (-1+ index2) (-1+ index2) (1+ n)))) (loop end1 end2 0))) (set! substring-match-backward (make-substring-match-backward char-equal?)) (set! substring-match-backward-ci (make-substring-match-backward char-equal-ci?))) (define string-equal?) (define string-equal-ci?) (define string-less?) (define string-less-ci?) (define string-match-forward) (define string-match-forward-ci) (define string-match-backward) (define string-match-backward-ci) (let () (define (string-comparison substring-comparison) (lambda (string1 string2) (substring-comparison string1 0 (string-length string1) string2 0 (string-length string2)))) (set! string-equal? (string-comparison substring-equal?)) (set! string-equal-ci? (string-comparison substring-equal-ci?)) (set! string-less? (string-comparison substring-less?)) (set! string-less-ci? (string-comparison substring-less-ci?)) (set! string-match-forward (string-comparison substring-match-forward)) (set! string-match-forward-ci (string-comparison substring-match-forward-ci)) (set! string-match-backward (string-comparison substring-match-backward)) (set! string-match-backward-ci (string-comparison substring-match-backward-ci))) ;;;; Character Search Primitives (define substring-find-next-char) (define substring-find-next-char-ci) (define substring-find-next-char-in-set) (let () (define (find-next char-test) (lambda (string start end key) (define (loop index) (and (not (= index end)) (if (char-test key (string-ref string index)) index (loop (1+ index))))) (loop start))) (set! substring-find-next-char (find-next char-equal?)) (set! substring-find-next-char-ci (find-next char-equal-ci?)) (set! substring-find-next-char-in-set (find-next char-set-member?))) (define substring-find-previous-char) (define substring-find-previous-char-ci) (define substring-find-previous-char-in-set) (let () (define (find-previous char-test) (lambda (string start end key) (define (loop index) (and (not (= index start)) (let ((index (-1+ index))) (if (char-test key (string-ref string index)) index (loop index))))) (loop end))) (set! substring-find-previous-char (find-previous char-equal?)) (set! substring-find-previous-char-ci (find-previous char-equal-ci?)) (set! substring-find-previous-char-in-set (find-previous char-set-member?))) (define string-find-next-char) (define string-find-next-char-ci) (define string-find-next-char-in-set) (define string-find-previous-char) (define string-find-previous-char-ci) (define string-find-previous-char-in-set) (let () (define (string-search substring-search) (lambda (string char) (substring-search string 0 (string-length string) char))) (set! string-find-next-char (string-search substring-find-next-char)) (set! string-find-next-char-ci (string-search substring-find-next-char-ci)) (set! string-find-next-char-in-set (string-search substring-find-next-char-in-set)) (set! string-find-previous-char (string-search substring-find-previous-char)) (set! string-find-previous-char-ci (string-search substring-find-previous-char-ci)) (set! string-find-previous-char-in-set (string-search substring-find-previous-char-in-set))) ;;;; Case (define substring-upper-case?) (define substring-lower-case?) (let () (define (substring-has-case? char-has-case?) (lambda (string start end) (define (loop index) (or (= index end) (and (char-has-case? (string-ref string index)) (loop (1+ index))))) (loop start))) (set! substring-upper-case? (substring-has-case? char-upper-case?)) (set! substring-lower-case? (substring-has-case? char-lower-case?))) (define substring-upcase!) (define substring-downcase!) (let () (define (substring-set-case! char-set-case) (lambda (string start end) (define (loop index) (if (not (= index end)) (begin (string-set! string index (char-set-case (string-ref string index))) (loop (1+ index))))) (loop start))) (set! substring-upcase! (substring-set-case! char-upcase)) (set! substring-downcase! (substring-set-case! char-downcase))) (define (substring-capitalized? string start end) (define (loop end) (or (= end end) (and (char-lower-case? (string-ref string end)) (loop (1+ end))))) (and (not (= start end)) (char-upper-case? (string-8b-ref string 0)) (loop (1+ start)))) (define (substring-capitalize! string start end) (substring-upcase! string start (1+ start)) (substring-downcase! string (1+ start) end)) (define string-upper-case?) (define string-lower-case?) (define string-upcase!) (define string-downcase!) (define string-capitalized?) (define string-capitalize!) (let () (define (string-op substring-op) (lambda (string) (substring-op string 0 (string-length string)))) (set! string-upper-case? (string-op substring-upper-case?)) (set! string-lower-case? (string-op substring-lower-case?)) (set! string-upcase! (string-op substring-upcase!)) (set! string-downcase! (string-op substring-downcase!)) (set! string-capitalized? (string-op substring-capitalized?)) (set! string-capitalize! (string-op substring-capitalize!)))  Date: 13 January 1985 16:28-EST From: Kent M Pitman Subject: policy to adopt To: SCHEME @ MIT-MC i think we should clearly re-emphasize what our rationale is for wanting any standard before deciding a policy. if the only purpose is to be able to write papers that use common syntax, the policy might want to be different than if we plan to port code. as much as we are not trying to make a Common Scheme, some of the trends in this discussion have leaned pretty strongly in that direction, perhaps overly so in some cases. i can elaborate if it becomes appropriate to do so; i'll let it go at that for now. -kmp  Date: 13 January 1985 16:22-EST From: Jonathan A Rees Subject: list-length To: willc%indiana.csnet @ CSNET-RELAY cc: SCHEME @ MIT-MC In-reply-to: Msg of Fri 4 Jan 85 15:50:17 est from Will Clinger Date: Fri, 4 Jan 85 15:50:17 est From: Will Clinger Pardon me, but I am not certain whether your recent message (LIST-APPEND, LIST-REVERSE, LIST-SORT, LIST-MAPCAR, ...) was in support of or in derision of the proposal that the operations on lists be prefixed by LIST- . Neither, really. The intent of the message was to ask "where do you draw the line?". E.g. APPEND / STRING-APPEND have the same problem that LENGTH / STRING-LENGTH do. If you're not careful you end up putting data type prefixes on everything (as you might have to in a strongly typed language without unions, like PASCAL) or nothing (as in a fully generic language like Smalltalk). What policy should we adopt?  Date: 28 December 1984 17:29-EST From: Jonathan A Rees Subject: list-length To: SCHEME @ MIT-MC In-reply-to: Msg of 23 Dec 1984 22:30-EST from mw%brandeis.csnet at csnet-relay.arpa LIST-APPEND, LIST-REVERSE, LIST-SORT, LIST-MAPCAR, ...  Received: from brandeis by csnet-relay.csnet id a026028; 24 Dec 84 2:15 EST Received: by brandeis.ARPA (4.12/) id AA06312; Sun, 23 Dec 84 22:31:44 est Date: 23 Dec 1984 22:30-EST From: mw%brandeis.csnet@csnet-relay.arpa In-Real-Life: Mitchell Wand,faculty Subject: list-length To: scheme@mit-mc.ARPA Cc: dyb%unc.csnet@csnet-relay.arpa Message-Id: <472707016/mw@brandeis> I vote for list-length over length, too. I think we just went over that one a little too rapidly at the meeting. -- Mitch Wand  Received: from unc by csnet-relay.csnet id ag24249; 23 Dec 84 11:49 EST Received: by unc (4.12/4.7) id AA29885; Sun, 23 Dec 84 01:06:25 est Date: Sun, 23 Dec 84 01:06:25 est From: Kent Dybvig Message-Id: <8412230606.AA29885@unc> To: scheme@mit-mc.ARPA Subject: length vs. list-length My primary concern about the length function seems to have been lost in the ensuing discussions. I do not insist that Scheme have a generic length function. I do insist that the naming convention for such functions be consistent. Doesn't this look a little odd? (define generic-length (lambda (x) (cond ((string? x) (string-length x)) ((vector? x) (vector-length x)) ((list? x) (length x))))) I like this much better: (define generic-length (lambda (x) (cond ((string? x) (string-length x)) ((vector? x) (vector-length x)) ((list? x) (list-length x))))) We have list-ref, vector-ref and string-ref. Why should we not have list-length rather than length? While we're at it, why not have list-append rather than append? Any functions that makes sense for strings, lists, and vectors should be named with the appropriate type-name prefix. (Even though the language doesn't specify a reverse function for vectors or strings and since such functions would be reasonable, the reverse function for lists should be named list-reverse.)  Date: Wed, 19 Dec 1984 12:12 EST Message-ID: From: CPH%MIT-OZ@MIT-MC.ARPA To: Scheme@MIT-MC Subject: Common Lisp order of evaluation In-reply-to: Msg of 19 Dec 1984 11:40-EST from Bert Halstead Come on, folks... This really isn't a great place to argue about what order Common Lisp evaluates its arguments. I for one don't care and would rather not know.  Received: by mit-vax.Mit-chaos.Arpa (4.12/4.8) id AA25785; Wed, 19 Dec 84 11:40:26 est Date: Wed, 19 Dec 84 11:40:26 est From: Bert Halstead To: KMP@mit-mc.ARPA, SCHEME@mit-mc.ARPA, dyb%unc.csnet@csnet-relay.arpa Subject: Re: Common Lisp order of evaluation The following passages out of the Common Lisp book may be relevant: "setf carefully arranges to preserve the usual left-to-right order in which the various subforms are evaluated." (p. 97) "Macros that manipulate generalized variables must guarantee the 'obvious' semantics: subforms of generalized-variable references are evaluated exactly as many times as they appear in the source program, and they are evaluated in the same order as they appear in the source program." (p. 99) -Bert  Received: from unc by csnet-relay.csnet id ac00691; 18 Dec 84 14:57 EST Received: by unc (4.12/4.7) id AA12698; Fri, 14 Dec 84 10:29:23 est Date: Fri, 14 Dec 84 10:29:23 est From: Kent Dybvig Message-Id: <8412141529.AA12698@unc> To: KMP@mit-mc.ARPA, SCHEME@mit-mc.ARPA Subject: Common Lisp order of evaluation If you have found passages which indicate a specific order of evaluation, I'd like to know about it. All that I have found in scouring the manual several times over, in several different versions over the years, is that arguments are 'processed' in left-right order *once they reach the caller*. Regardless of any intentions on the designer's part, or any decisions of discussion groups, any feature of the language that is not spelled out is subject to interpretation. There is certainly nowhere in the manual that states that arguments are evaluated in left-right order.  Date: 17 December 1984 18:23-EST From: Jonathan A Rees Subject: continuation terminology To: cth%indiana.csnet @ CSNET-RELAY cc: SCHEME @ MIT-MC In-reply-to: Msg of Sun 16 Dec 84 17:27:40 est from Chris Haynes Date: Sun, 16 Dec 84 17:27:40 est From: Chris Haynes We object strongly to the use of the term "escape procedure"... My only objection to the terms "continuation" and "continuation object" is that the presence of fluids and/or UNWIND-PROTECT mean that the thing created by the user-visible CATCH or CONTINUE or CALL-... or whatever is more than just a continuation, since it implicitly includes a reference to the dynamic state. There is a low-level thing which really does give you a continuation, but I expect that the mechanism which does the right thing with fluids would be the normal one used by users. I don't have any new alternative term to propose, however. Of course, we decided not to decide whether or not the essential scheme's continuation procuration combinator dealt properly with fluids, since essential scheme doesn't deal with fluids at all. It seems to me that any fluid mechanism whatsoever must distinguish between continuations and continuation+state things. As things are, my guess is that it would be up to the implementor which of these two things the essential dialect's CALL-... gives you. (This might be the wrong thing, but I won't argue that position here - this message is already too long.) (By the way, no Lisp has never had escape procedures per se; the usual CATCH/THROW mechanism doesn't introduce a new kind of object. I'm not convinced that the term "escape procedure" is as broken as you think it is, or that it necessarily has such strong connotations. But I'm not partial to the term.)  Received: from indiana by csnet-relay.csnet id a001364; 16 Dec 84 23:35 EST Received: by iuvax.UUCP (4.12/4.7) id AA04292; Sun, 16 Dec 84 17:27:40 est Date: Sun, 16 Dec 84 17:27:40 est From: Chris Haynes To: scheme@mit-mc.ARPA Subject: continuation terminology We object strongly to the use of the term "escape procedure". The word "escape" is far to limiting to describe continuations, which are good for so much more. Also, the term "escape procedure" is strongly associated with the limited facility by that name provided by most Lisp systems (which isn't good for much besides "escaping"). If we adopt the old Lisp name, many will assume that continuations aren't good for anything more than Lisp escape procedures, and will miss what we feel is one of the most important distinguishing features of Scheme. We find the term "continuation" to be quit satisfactory in most contexts, and it agrees with the name "call-with-current-continuation". However, we recognize that in the context of denotational semantics or implementation discussions, there is possibility of confusing continuations as first-class programming objects and their semantic or implementation counterparts. Thus additional terminology is desirable when such distinctions must be made, and to standardize such terminology it should probably be used in the Revised Revised Report. We suggest the term "continuation object" for this purpose, though it is not wonderful and we welcome other suggestions. -- Chris Haynes Dan Friedman Eugene Kohlbecker  Date: Sat, 15 Dec 1984 19:53 EST Message-ID: From: CPH%MIT-OZ@MIT-MC.ARPA To: linus!ramsdell%UUCP@YALE.ARPA (John D. Ramsdell) Cc: T-Discussion%MIT-OZ@MIT-MC.ARPA, Scheme@MIT-MC Subject: Scheme bibliography In-reply-to: Msg of 14 Dec 1984 13:44-EST Fri 14 Dec 84 09:26:47 est from linus!ramsdell at Mitre-Bedford, linus!ramsdell%UUCP at YALE.ARPA (John D. Ramsdell) I believe that this is a complete list of the early Scheme papers: Steele, Guy Lewis, Jr., and Gerald Jay Sussman. 1975. Scheme: An interpreter for the extended lambda calculus. Memo 349, MIT Artificial Intelligence Laboratory. Steele, Guy Lewis, Jr., and Gerald Jay Sussman. 1976. Lambda: The ultimate imperative. Memo 353, MIT Artificial Intelligence Laboratory. Steele, Guy Lewis, Jr. 1976. Lambda: The ultimate declarative. Memo 379, MIT Artificial Intelligence Laboratory. Steele, Guy Lewis, Jr. 1977. Debunking the "expensive procedure call" myth. In Proceedings of the National Conference of the ACM, pp. 153-62. Steele, Guy Lewis, Jr., and Gerald Jay Sussman. January 1978. The revised report on Scheme: A dialect of Lisp. Memo 452, MIT Artificial Intelligence Laboratory. Sussman, Gerald Jay, and Guy Lewis Steele, Jr. May 1978. The art of the interpreter or, The modularity complex. Memo 452, MIT Artificial Intelligence Laboratory. Steele, Guy Lewis, Jr. May 1978. Rabbit: A compiler for Scheme. Technical report 474, MIT Artificial Intelligence Laboratory.  Date: 12 December 1984 21:37-EST From: Kent M Pitman To: dyb%unc.csnet @ CSNET-RELAY cc: SCHEME @ MIT-MC Actually, Common Lisp does specify the order of evaluation as left to right. It's tricky to find the passages in the CL manual which assure this, but they are there if you look hard enough. The issue came up recently in the Common Lisp discussion group and subsided after various obscure but recently conclusive passages were cited. Anyway, I agree it is reasonable to leave the order unspecified. It may be computationally infeasible for the compiler to determine when order of evaluation matters in many situations where "better code" would be available if such a determination could be made.  Received: from unc by csnet-relay.csnet id aa04450; 12 Dec 84 15:04 EST Received: by unc (4.12/4.7) id AA29958; Tue, 11 Dec 84 00:04:48 est Date: Tue, 11 Dec 84 00:04:48 est From: Kent Dybvig Message-Id: <8412110504.AA29958@unc> To: ANDY@su-score.ARPA, scheme@mit-mc.ARPA Subject: Re: Scheme conference report Relative order of evaluation of the expressions in a combination is not specified for at least two reasons: 1) It is poor coding style to depend on order of evaluation. If there is an ordering constraint it should be made explicit by using "let" or some such. 2) It is a severe constraint on the implementation for the language to specify an evaluation order. What is easy for one machine model or architecture may be difficult for another. Note that Pascal and Scheme are not alone; neither C nor Common Lisp specify the order of evaluation. Kent Dybvig  Date: 10 Dec 1984 12:24 EST (Mon) Message-ID: From: Bill Rozas To: Andy Freeman Cc: scheme@MIT-MC.ARPA Subject: Scheme conference report I would like to clarify (hopefully) a few points with respect to the evaluation of combinations: The operator position is a "distinguished" argument. In my opinion it is the only position for which the evaluation order might be relevant. If reflective procedures are present in an implementation, then the operator position must be evaluated before any operand expressions. Since in the common subset there is no provision for reflective procedures, there is no need to specify any particular order with respect to the operator. In Will's last message there was a statement to the effect that normal order evaluation was prohibited. I find this rather surprising. One of Guy Steele's Rabbit compiler main points was that normal order beta-subtitution could be used very successfully to optimize Scheme code. In the absence of side-effects, and if the programs terminate when using applicative order, there is no way to determine whether normal or applicative order is used. A compiler can use this profitably to postpone evaluation. A think that a more appropriate statement is that portable programs should not depend on the evaluation strategy used, ie. they should work in applicative order, but different implementations may want to use different strategies. The order of argument evaluation was left unspecified for various reasons: - The order of argument evaluation is only relevant in the presence of side-effects in some of the argument expressions. Scheme is predominantly an applicative language (contrast with Pascal), and good style requires that arguments to procedures (operands of combinations) be side-effect free. By not specifying the order, it was felt that code with such side-effects would be discouraged since its effect would not be predictable. - An optimizing compiler can do a better job if it has freedom over the order in which it can execute expressions. Leaving the order unspecified potentially allows a clever compiler to choose the optimal order for each combination. Note that the meaning of a program can only be changed by reordering if the argument expressions contain side-effects. But this code could not be guaranteed to work in other implementations with different default order for argument evaluation. - Leaving the order of argument evaluation unspecified allows a parallel implementation to evaluate in parallel. Note again that the only programs whose semantics are not clear (and are therefore not predictable) are those with side-effects in the operands of a combination. A marginally related issue is the issue of macros. In Scheme there is not so much of a need to provide a macro facility as in other dialects of Lisp. Macros are needed only to provide "nicer", more readable syntax. In other dialects they are needed to extend the langauge since there is no way of encapsulating an expression and an environment. Freezing ("thunkifying", wrapping in a lambda expression) an argument encapsulates a context and an expression in a procedure which can then be invoked at will by the operator procedure. While syntactically clumsy, it is conceptually very elegant. Note that an optimizing compiler (Rabbit, for example) can eliminate most or all of the overhead of freezing and thawing.  Date: Sun 9 Dec 84 16:45:14-PST From: Andy Freeman Subject: Scheme conference report To: scheme@MIT-MC.ARPA Will the final report on the scheme conference say anything about macros? There are substantial issues to be resolved/understood, especially in scoping and expansion, but can a "least common denominator" solution be released in the interim? How about a syntax for defining top-level macros? That can be useful even if everything else is undefined or optional. Speaking of top-level solutions, why was ",." left out of the backquote syntax? (It's like ",@", except that the value of the following form may be destructively spliced into the result. ",@", as you recall, non-destructively splices the value of the following form into the result.) If deeper levels of nesting are going to be optional, the report has to define what happens. By the way, unless Scheme is going to define a character set, EOF isn't necessarily a character. Neither is end-of-line. Are all symbols interned or not? (Interning isn't necessary to print code and be able to read it back in later.) Still, most of these are minor issues. What the final report really needs is a discussion of the decisions. Some things do not need explanation (except to historians), like the names lambda and ', but, for instance, why are true and false self-evaluating? In many cases the decision itself is unimportant, but the issues are. The report would also be improved by explicit mention of controversial issues. So there's no decision; the reasons are still important. -andy ps - Why is argument evaluation order unspecified? That was a mistake in Pascal, but then AND/OR/etc. have a defined order in Scheme. Then again, so many standard forms have a right-left defined order that consistency would suggest that user procedures should also. Is the closure position an argument? -------  Received: from indiana by csnet-relay.csnet id ab22287; 9 Dec 84 14:36 EST Received: by iuvax.UUCP (4.12/4.7) id AA01074; Sun, 9 Dec 84 11:11:13 est Date: Sun, 9 Dec 84 11:11:13 est From: Will Clinger To: scheme@mit-mc.ARPA Subject: apology to Franz Lisp I apologize to Franz Lisp for claiming that not every interned symbol in Franz has a printed representation that reads back in as the same symbol. An example confused me but not Franz Lisp. Peace, William Clinger  Received: from indiana by csnet-relay.csnet id a022287; 9 Dec 84 14:34 EST Received: by iuvax.UUCP (4.12/4.7) id AA06716; Sat, 8 Dec 84 22:50:32 est Date: Sat, 8 Dec 84 22:50:32 est From: Will Clinger To: KMP@mit-mc.ARPA Subject: Re: critique of preliminary report Cc: scheme@mit-mc.ARPA I am posting this in response to Kent Pitman's excellent critique of the preliminary report on the October workshop. ERRORS AND OVERSIGHTS I should have said: Numbers, strings, characters, and #!TRUE and #!FALSE are self-evaluating, which means they need not be quoted. Symbols and pairs are not self-evaluating. It is not specified whether other things are self-evaluating. The semantics of (BEGIN) is not specified. TRUNCATE could be defined by (DEFINE TRUNCATE (LAMBDA (X) (IF (NEGATIVE? X) (- 0 (FLOOR (ABS X))) (FLOOR X)))) VECTOR-REF takes a vector v and an nonnegative integer n such that n < (VECTOR-LENGTH v) and returns ... . ---------------------------------------------------------------- WHAT DOES "OPTIONAL" MEAN? The intent of the workshop was that extensions must not conflict with optional features, and I should have said so. In fact some features were made optional in order to prevent dialects from using certain syntaxes for other purposes. It follows that #b, #o, #d, #x, #\, and so on can't be used for any purpose other than to support the optional feature. I agree that the "optional" stuff about NIL and T is ridiculous. What I should have said instead is that implementations that want to treat NIL and T as constants can do so. In effect this is a warning against using NIL and T to name variables. Note that (FOO . NIL) cannot read the same as (FOO) no matter what. I didn't mean to say that "optional" names or features are to be preferred to essential names or features, and I don't think I did. ---------------------------------------------------------------- POOR WRITING IN THE PRELIMINARY REPORT I agree with Kent Pitman's points regarding the use of "...", "mistake", "respectively", and "double quote". I agree that the set of single character tokens needs to be better defined. I would like to include appendixes in the final report with more rigorous descriptions of the lexical syntax, the context-free syntax, and the denotational semantics of Scheme. How about "The order of evaluation within a procedure call is not specified"? That isn't quite true, of course -- normal order evaluation is prohibited. I should have said that some single object represents both false and the empty list. There may be other objects representing false. Might there be other objects representing the empty list? (I hope not.) I agree that it would be better to have said that (NUMBER? x) doesn't imply (INTEGER? x). I hope everyone noticed that it is an error to take the CAR or CDR of the empty list. The definition of a proper list in the preliminary manual was a joke. The notion of a proper list has to do with finiteness, which is not first order definable. ---------------------------------------------------------------- LEXICAL MATTERS It is not specified whether single quote, backquote, sharp sign, and vertical bar are delimiters that terminate symbols. The status of these characters would be decided by a general rule that emerged during discussion at the workshop, but not everyone agreed to the general rule. The general rule is: Special characters that come in pairs (left and right parenthesis, left and right bracket, left and right curly brace, doublequote) should be delimiters while other special characters (period and so on) should be preceded by a space if they are to be used in their special sense. Semicolon isn't really an exception to the pattern because the end of line would be a delimiter anyway. Whether vertical bar should be a delimiter according to the rule depends on whether vertical bar is a special character that comes in pairs, which is not specified. According to the rule single quote, backquote, and sharp sign should not be delimiters. > * I agree with reserving {, [, ], and }, but I would specify that they > may be used as alphabetic according to syntactic escape conventions. I don't understand "...according to syntactic escape conventions". I have yet to hear of a good use for slashification of symbols. If there is no compelling need for a feature, we should leave it out. The workshop allowed slashification but did not encourage it. > * I notice that the space of symbol names is highly constrained for > the "required subset". A property, however, that should be required > is that within any given dialect, every interned symbol (no matter what > characters it contains) must have a printed representation which is > read-invertable within that dialect. I suspect that all dialects > do this already anyway, but it should be a guaranteed property of the > language since programmers will tend to depend on such things and should > have a guaranteed semantics backing them up. We probably ought to require something along these lines, but the property you desire is too strong. It is enough that every symbol read by the reader be printed in a form that will read back in as the same symbol. Other symbols are too random to worry about. In fact, I would be satisfied if only the required set of symbol names have the property you desire. For what it's worth, Franz Lisp does not have the property, but I doubt that its lack is a significant cause of dissatisfaction with Franz Lisp. ---------------------------------------------------------------- SPECIAL FORMS LETREC vs LABELS, REC vs LABEL, and SET vs SET! are matters of taste that needed to be decided one way or the other, and were. History plays a significant role in decisions such as these. The first two matters had history on both sides, but the history of SET in traditional Lisp counts against it. In some cases the IF, COND, and CASE special forms return unspecified values. The SET!, DEFINE, and DEFINE! special forms, and the SET-CAR!, SET-CDR!, and VECTOR-SET! procedures, always return unspecified values. Except for the result of the DEFINE form, I wrote that it is a "mistake" to use these undefined values, and except for the results of the DEFINE and DEFINE! forms I wrote that implementations could signal an error if the values that were returned were "used". What I wrote is faithful to the workshop's decisions, but I can be accused of deviating in the cases of DEFINE and DEFINE! . Kent is right to question what we meant by "using" a value. The answer is that we don't know. Obviously an implementation could return a strange value that would cause an error to be signalled if an attempt were ever made to take its CAR or its successor; an implementation could also arrange for an error to be signalled if the value were ever an operand to EQ? or CONS; and there are no doubt other things that an implementation could do as well. I will not offer a formal description of the circumstances under which an error could be signalled, because I want to leave room for implementations to experiment with different approaches. A DEFINE form is at "top level" iff it it not nested within any other form. This definition clearly establishes circumstances that do not count as top level, but it does not establish any circumstances that do count as top level. I think each implementation will have to specify circumstances under which a DEFINE form has the described semantics, and those circumstances would then count as top level for that implementation. The Abelson and Sussman book uses DEFINE inside LAMBDA as syntactic sugar. Scheme's future is tied to the success of that book, so the sugar was dignified with "optional" status. The optional status of the sugar required that ordinary DEFINE be restricted to "top level". The (DEFINE (fn . args) . body) sugar was dignified with "optional" status for the same reason. Kent has acquired a taste for this sugar, but I consider it a violation of orthogonality that is doubly pernicious: (1) it discourages programmers from thinking of procedures as objects distinct from their names; (2) it discourages programmers from using procedures with local state. I agree that the optional status of named LET should imply an optional status for named LET*, or else both named LET and named LET* should be left out altogether. By the way, David Bartley asks whether the body of a named let is within the scope of the name. The Revised Report has the body within the scope of the name. Does anyone want to argue that the body should be outside the scope of the name? When it was asked if DO should bind RETURN, someone said "Of course not!", and that was the end of the discussion. If DO were to bind RETURN I believe that DO would be the only construct in even the optional language to bind an identifier that does not appear explicitly in the code, and I see no reason to condone that kind of anomaly. ---------------------------------------------------------------- DATATYPES I was disappointed that we were unable to agree that any datatypes were disjoint. The fact that the workshop participants insisted on a special note to the effect that characters need not be a distinguishable data type leads me to believe that despite their votes most participants assumed that other data types were distinguishable. In the final report I intend to recommend that at the very least numbers, symbols, and pairs should be disjoint; does anyone object? Even without disjointness of data types, the NUMBER? and INTEGER? predicates are useful because they define the domains of other procedures. Thus if all strings are numbers then it must be possible to subtract strings. The nonsense to the effect that pairs might be indistinguishable from vectors of length 2 is to remind folks not to assume that pairs and vectors are disjoint. I agree that the note is out of place and ugly. I expect streams will be a Scheme data type, but as Kent points out they might overlap with (say) numbers. Nonetheless the STREAM? predicate will be useful because anything of which the STREAM? predicate is true will have to support the operations on streams. ---------------------------------------------------------------- PROCEDURES I think it went without saying that escape procedures can be called more than once. We didn't feel any need to say that the addition procedure could be called more than once, either. The generalizations of +, -, *, and / to arbitrary arity must follow Common Lisp, so the ambiguity is not great. There is some ambiguity, however, because Common Lisp has all sorts of rules about integers, rationals, floats, and complexes that don't apply to Scheme. MIN and MAX are restricted from arity 0 because some implementations don't have a smallest or largest representable number. Implementations should not be allowed to signal an error on something like (=? 1 1.0). I don't think we should specify that the result is true because you might want to implement a Scheme in which "approximations" are a subtype of the numbers, in which 1.0 is read in as an approximation, and in which all equality comparisons involving approximate numbers return false. (If you haven't caught on by now, I'm perfectly comfortable with the fact that Scheme is far less tightly specified than Common Lisp. I believe Scheme can continue to represent the future of Lisp only by being open to experimentation.) I would rather not talk about "interning" a symbol, because there is no need to talk about it when all symbols are interned. I have to use words like "interned" to talk about implementations in which not all symbols are interned, but for definitions I would like to refer people to manuals for traditional Lisps. LENGTH is defined on proper lists, and its action on anything else is not specified. I can't imagine a definition of APPEND! that would want to mutate its last argument either. Shall we say it doesn't? Kent's suggestions for MEMQ?, MEMV?, MEMBER?, ASSQ?, ASSV?, and ASSOC? are interesting. Several people at the workshop, notably Hal Abelson, expressed the belief that we ought to reconsider the entire MEMBER/ASSOC complex of procedures. MAPCAR and MAPC have their historical names for historical reasons, and I would not be averse to renaming them eventually. If MAPCAR is defined as follows in an implementation that evaluates right to left, then the list of results is constructed from right to left: (DEFINE MAPCAR (LAMBDA (F L) (IF (NULL? L) '() (CONS (F (CAR L)) (MAPCAR F (CDR L)))))) I agree with Kent that the result of VECTOR->LIST cannot reasonably be guaranteed to be a new object. VECTOR-SET!, SET-CAR!, and SET-CDR! seem inconsistent to me too. I think we should offer a prize for the best rationalization of these names. I think garbage collection should be treated as an issue of performance rather than as an issue of semantics. Peace, William Clinger  Date: 8 December 1984 18:55-EST From: Kent M Pitman Subject: MIN/MAX, DO/RETURN To: SCHEME @ MIT-MC References: Msg of 6 Dec 1984 22:02-EST from Kent M Pitman Jonathan Rees had a good points about a couple of my earlier remarks... * Making MIN/MAX return smallest/largest representable number would be infeasible in implementions with bigfloats or bignums. I should have thought of that. (I must have been thinking too hard about the flonum case; sorry.) I'll withdraw that suggestion. * Making DO bind RETURN would have been a bad idea since it would "wire" the name RETURN. I withdraw my disparaging remarks about its not binding that name. -kmp  Date: 7 December 1984 16:26-EST From: Jonathan A Rees Subject: Slashification To: KMP @ MIT-MC cc: SCHEME @ MIT-MC In-reply-to: Msg of 6 Dec 1984 22:02-EST from Kent M Pitman I think I was the one who suggested that there be neither slashification nor vertical barring. If you tell the T printer not to use either \ or | and it comes across a symbol which requires quoting anyhow, it prints #[Symbol "foo"]. (If you have \ but not |, as in T, you need to have a way to print the null symbol anyhow; it really does come out as #[Symbol ""] - try it.) If print tables exist or if STRING->SYMBOL accepts any string whatsoever, then it is necessary to have some way for unusual symbols to read and print, but there's not really any need to wire down a special character for the purpose, because e.g. some # syntax could be used. Some people wanted to throw away \ in favor of just | ; people didn't agree when I suggested going with \ but not | ; so we just decided that the need was unimportant enough that neither syntax was required. I'm not convinced that there needs to be a defined, portable way to print unusual symbols, although maybe you could talk me into such a position. Jonathan  Date: 6 December 1984 22:02-EST From: Kent M Pitman Subject: [willc: preliminary report of workshop] To: willc%indiana.csnet @ CSNET-RELAY cc: SCHEME @ MIT-MC In-Reply-To: <8411120256.AA05811@iuvax.UUCP> Ok, Will, I'm assuming things decided at the meeting are not cast in concrete. I certainly take issue with a number of the decisions. In a few places, I also pick on your notation a bit when I think its unclear. Thanks for taking the time to write up everything. Here come the comments... * I dislike the description of "optional features". In particular, you say: Optional features may not be supported by every implementation, but those that do support a feature will use the same syntax and semantics for the feature. Hence code that makes use of optional features will run on any implementation of Scheme that supplies the optional features. .... An implementation may extend the language in any way whatsoever, but code that makes use of extended features is not portable. These two paragraphs are in conflict and allow for lots of inconsistency which I will identify at appropriate points later. The principle troubles, though, are: * Since a language can be extended in "any way whatsoever", can such extensions be syntactically in conflict with optional language features? eg, if #\ is optional and my dialect doesn't use it, can I then define my own #\ as an extended part of the required subset? * In some cases, "required" language features can be redefined incompatibly by "optional" features. eg, (COND (FOO => X)) has a well-defined semantics regardless of whether the optional "=>" feature of COND is present. However, that semantics is not the same in the two cases. The point I'm trying to make is that saying something is "optional" says little if anything unless you at least define that if anyone uses the syntax corresponding to the optionality in a dialect which doesn't support the feature, that he is in error. Put another way, extensions may not invalidate optional features. On the other hand, if this were stipulated the list of "optional" features to which I objected would be quite lengthy. * I disagree with calling the string quote character "double quote". I prefer "doublequote". Since there is a character named "quote", the phrase "double quote" might designate '' instead of ". * In discussing what terminates tokens, you should say which of these characters (presumably all) are also single-character tokens. In particular, that "((A B)C)" is tokenized {"(" "(A" " " "B" ")C" ")"} or {"(" "(" "A" " " "B" ")" "C" ")"} hangs in the balance. I'm sure no one disagrees, but if you're not going to be complete about these things, you are sort of wasting your time just trying to look formal about things. * I agree with reserving {, [, ], and }, but I would specify that they may be used as alphabetic according to syntactic escape conventions. Optionally, the following characters may be delimiters that terminate symbols: * What does it mean to say single quote, backquote and sharpsign may "optionally" terminate tokens? It means expressions like (JOHN'S COAT) read differently in the different dialects. How is this distinct from saying "Optionally, the following characters may be delimiters that do not terminate symbols:"? It only makes sense to say something is optional if it's going to mean that when it's present. I bet some dialects treat (JOHN'S COAT) as a two-list and others treat it as a three-list. Hence, any description of this relation between dialects can at best say: "The specification takes no stand on the issue of whether the following delimiters terminate symbols. Any use of expressions like (JOHN'S COAT) are to be considered non-portable." * I would personally prefer if vertical bar had been defined to be alphabetic, but I am at least happy that it is "not specified" what its meaning is rather than that it is "optional". * I strongly oppose the idea of not specifying an escape char for symbols. You say there is "widespread agreement that ``slashification'' of characters within symbols is a relic that ought to be abandoned." I am not party to such agreement. I strongly oppose the idea of eliminating slashification. The Maclisp conventions of vertical bars made up for the absence of strings. With their passing, syntactic quoting of lots of chars is very rare, and in those few cases, I think slashing works fine. It also is a low-cost mechanism for the printer, since no lookahead is required. Also, it is uniform with respect to strings, which already use slash for special chars anyway. Finally, without slash, there would be no way to get vbars into symbol names, since vbars cannot adequately quote themselves. On the other hand, slash can work fine in the absence of vbar. So if one thing is to go as a readsyntax quoter, it should be vbar. * Will-- Your meta-syntactic use of "..." in a description of what the "." character does is very confusing. * Does anyone mind if (. A) is the same as A? It has a certain elegance to it if you think about it. (CONS 3 (CONS 2 (CONS 1 0))) => (3 . (2 . (1 . 0))) => (3 2 1 . 0) (CONS 2 (CONS 1 0)) => (2 . (1 . 0)) => ( 2 1 . 0) (CONS 1 0) => (1 . 0) => ( 1 . 0) 0 => 0 => ( . 0) Just a thought. * I find #!true and #!false to be ugly and visually confusing with the popular convention of "!" designating something destructive. It would make more sense for #! to be saved for something like #. in Common Lisp. I agree #TRUE is reasonable. I'd have preferred #: for this. * What does it mean to say: "Optionally, binary numbers may be written using the #b notation. Optionally, octal numbers may be written using the #o notation. Optionally, decimal numbers may be written using the #d notation. Optionally, hexadecimal numbers may be written using the #x notation." Presumably this means that dialects not wanting #B, #O, etc. can use these to mean other things. * What does it mean to say: "Optionally, special characters may be written using the #\ notation. If this feature is supported, then the Common Lisp names for special characters must be supported." Presumably this means that if I don't want to be able to write special characters, I can make #\ do something else. In fact, if I want to use other names than those used by Common Lisp, I can just "not support" this feature and then "make any extension whatsoever" to my dialect such that #\ does something completely different, like understand a different set of character names. * Was anything decided about whether #!TRUE and #!FALSE would self-evaluate or whether they required quoting? * Was anything decided about whether numbers must self-evaluate or whether a dialect may require quoting? * Since "optionally, numbers may be written using decimal points and/or exponents", does this mean that numbers with decimal points are integers or floating? Does it mean that if I don't support the feature that I can take the alternate position? * I notice that the space of symbol names is highly constrained for the "required subset". A property, however, that should be required is that within any given dialect, every interned symbol (no matter what characters it contains) must have a printed representation which is read-invertable within that dialect. I suspect that all dialects do this already anyway, but it should be a guaranteed property of the language since programmers will tend to depend on such things and should have a guaranteed semantics backing them up. * What does it mean to make NIL optionally evaluate to the empty list or optionally evaluate to false. What happens if I make it false and then try to run my code in another dialect where it's the empty list and where the two are not the same thing. The definition of optionality says that code written in the optional subset will run correctly in another dialect supporting optional features. It doesn't seem to me like a good idea to optionally define a symbol as able to take on several values and then be able to write meaningful code. * It is specified that "the order of evaluation within an application is not specified". I would prefer "combination" or "expression" to "application" as a matter of terminology to avoid confusion with the application that happens in the APPLY function, which doesn't involve evaluation at all. * I don't like the name LETREC; I preferred LABELS. Neither is very suggestive of anything; the latter is at least a real word. * Will-- I don't like the use of the term "mistake" throughout the report, at least without defining it formally. In my dialect, it connotes an unintentional error and it seems to me that if the user intentionally did the offending thing, it would not be a mistake. I would say "error" in its place, or define the term "mistake" formally early on. * I disagree with the various forms that claim it to be a "mistake" to use certain return values, allowing some implementations to signal an error. I don't agree that such errors can ever be detected at the language level; I would like a formal description of exactly when it is believed that such an error could be signalled. The forms in question are: IF, COND, SET!, DEFINE, DEFINE!, CASE, SET-CAR!, SET-CDR!, and VECTOR-SET!. * The semantics of (COND (X => Y)) is messy due to optionality as described earlier. * Will-- I would name the ... sequences in definitions of things like LET, COND, etc which use multiple sequences. I realize you use them right to left, but that could be made more apparent. Perhaps ..foo.. instead of ... * I find the name SET! both ugly and redundant. The "!" convention as originally created by the T people identifies a destructive variant of an otherwise-non-side-effecting operation. So, for example, APPEND and APPEND!, etc. Logically, there could be a CHANGE-CAR and CHANGE-CAR!, one of which was (LAMBDA (C V) (CONS V (CDR C))) and the other which was (LAMBDA (C V) (SET (CAR C) V) C) In any case, I strongly think that the primitive for assigment should be SET and not SET!. In fact, since no one likes assignment anyway, I don't see any reason why anyone should object to just leaving this undefined in the standard. It would only discourage people from writing destructive code. But I would be very unhappy to see T change the name of SET to SET!. Similarly, I strongly dislike the name SET-CAR! and SET-CDR!. * The definition of DEFINE refers to the "top-level" definition of a variable. I don't believe it's established what "top-level" means, so this definition is pretty muddy. Further, what is the implication of this definition upon doing (LAMBDA (X) (DEFINE X X))? I am very discouraged that the (DEFINE (fn . args) ...) syntax isn't required. This means that any portable code must be ugly, meaning no one is likely to ever write truly portable code, meaning this standard is a farce. * It is silly to require that there be at least one form in a (BEGIN). It is easy for macros to come up with situations where there are no forms to put there and as long as the macro's caller doesn't depend on the value, it shouldn't matter. The return value of a BEGIN with no forms should just be undefined. * The fact that (LET* ...) cannot admit an optional name reveals an asymmetry which I find very distasteful. I suggest that named LET be left to implementors as an "arbitrary" extension not to be mentioned in any common subset. * I would prefer to have REC be called LABEL. Again, at least it's English. * I don't see any good reason to have DO not bind RETURN. Can someone elaborate on that? * The description of DEFINE inside LAMBDA is inconsistent with the earlier description of DEFINE as creating a toplevel definition. I think this should be a non-standard extension. I see no reason to dignify it with any "optional" status. * The term "top-level binding" is again completely vague in DEFINE!'s definition. * The definition of optionality specified that if an optional feature was present, the dialect should prefer to call it by the "optional" name. This is somewhat inconsistent with making SEQUENCE an optional synonym for BEGIN. Since it is not encouraged for use and is not going to exist in all dialects, is there any sense to including it here? * The entire section on datatypes is hopelessly muddled. About the only useful thing said is that anything which is a first class object must have unlimited extent. * In the sentence "There is an object which represents both false and the empty list", I cannot discern whether that means there may/must be one/two objects filling that description. Shouldn't we say, "False and the empty list must be represented as first class objects and that object {may,must} [not] be distinct." or some such. * Since datatypes are not declared to be disjoint, it isn't necessary to mention that characters may be represented as numbers, except perhaps as a footnote to remind the forgetful reader. Strings can be represented as numbers, too, the way things are written. * Was there really anyone who thought streams shouldn't be first class objects? Since datatypes aren't disjoint and such objects could be indistinguishable from numbers or arrays or whatever, is there really a reason to care? * The unary procedure not should be defined to return "a true value if its argument is false and a false value if its argument is not false." ... rather than "if its argument is true." for the second part. * I suggest renaming CALL-WITH-CURRENT-CONTINUATION (or CALL/CC) to just CONTINUE. eg, (CONTINUE (LAMBDA (C) (IF (FOO) (F C) (G C)))) Anyone else support this? * By the way, saying the escape procedure has unlimited extent doesn't say it can be called more than once. Does everyone agree to either stipulate that or not? * If "the unary predicate NUMBER? is true of numbers and false of everything else" and "the unary predicate INTEGER? is true of integers and false of everything else", I don't suppose this says much since types are not disjoint and so strings are not necessarily not numbers and need not necessarily cause INTEGER? or NUMBER? to return false. Certainly characters needn't yield false from NUMBER? or probably from INTEGER?. As such, these predicates are of limited value. * Of what point is it to make claims about what "almost all implementations" will do for real numbers? Either they're required to or they aren't. The rest belongs in some other document. * I don't agree that allowing generalization of +, -, *, and / to arbitrary arity is a good idea or even well-defined. eg, the proper generalization of - to arity 1 is (- 3) => 3, not (- 3) => -3. Hence, specifying that unary negation is optional is in conflict with specifying that - may be generalized. * Will-- The discussion of QUOTIENT/REMAINDER and of CONS/CAR/CDR should use the word "respectively" in the appropriate places. When I first read that QUOTIENT and REMAINDER return the quotient and remainder, I spent an unduly long time flipping back pages looking to see if you'd allowed multiple values before I realized that it was silly for both these functions to do the same thing or for that same thing to be what it had first looked to me like they're doing. * I don't see why MIN/MAX should be restricted from arity 0. They should just return the smallest and largest representable numbers. I guess as long as they aren't defined to signal an error in this case, individual dialects could be extended anyway. * It should be made explicit whether (= 1 1.0) is defined to work. Note that this may be tricky since even (= 1.0 1.0) won't necessarily work if the 1.0's were computed rather than read and have different bit patterns that are too tiny to make a difference on output. * It is silly to specify that implementations may "optionally" support numbers that are non-integers. Why not just define that (NUMBER? x) doesn't imply (INTEGER? x). That definition wouldn't mean that every number wasn't an integer, it would only mean that every number wasn't necessarily a number. Specifying that "almost all implementations" will support this option is again silly and might in pathological situations be misleading. * Is the definition of (TRUNCATE x) really correct? It looks like it must be screwed up on the negative side near 0. eg, (TRUNCATE -0.5) doesn't have the same sign as 0.5 does it? Or is there a negative 0? * The meaning of "interning" a symbol should be specified. * It should be stated explicitly that CAR and CDR of the empty list is not defined. * What's this nonsense about pairs being maybe indistinguishable from vectors of length 2. Is there a good reason for that? It doesn't really matter since numbers haven't been defined as distinguishable from strings either, but it somehow offends my sense of aesthetics to see this note here. Is this due to some problem with Maclisp HUNK2's or something unrelated? * In "The following descriptions use the notion of a proper list. The set of proper lists is the smallest set satisfying: the empty list is a proper list a pair x whose CDR is a proper list is a proper list, provided (MEMQ x (CDR x)) is false." I think MEMQ isn't the function that you want, but I find it amusing to see the language defined meta-circularly in this way (since MEMQ is almost certainly defined to terminate only on proper lists and may even want to type-check proper-list-ness). * Is the function LENGTH defined to err or to not return when given a circular list? What about an otherwise improper (ie, dotted) list? * The definition of APPEND is poor. It should be defined with NAMED-LAMBDA for safety in situations where APPEND gets redefined. Also, its text description is too windy. * I see no reason for APPEND! to be defined to possibly side-effect either arg. This may force lots of needless copying in order to write provably correct programs. I can't imagine a definition of APPEND! which would want to destructively modify its last argument. * All these definitions (APPEND, REVERSE, ...) are ugly due to the silly restrictive version of DEFINE. I certainly wouldn't want my students programming like that. * It should be stated in English what happens if LIST-REF and LIST-TAIL fall off the end. I assume it follows from the definitions of CAR/CDR that such is a signallable error. * There should be MEMQ?, MEMV?, and MEMBER? to match MEMQ, MEMV, and MEMBER. This enhances garbage collection since if these functions are only being used for truth value, you don't want to hold pointers to potentially large list structures. Also, it enhances debugging since if F is a function on booleans, (F (MEMQ X Y)) will receive true/false rather than a list or false. Ditto for ASSQ?, ASSV?, and ASSOC?. * You specify no order of evaluation for MAPCAR. I think you mean no order of "application". * I dislike the asymmetry between MAPCAR and MAPC. MAPC has no defined return value, MAPCAR does. MAPC has defined order of application, MAPCAR does not. In short, they have nothing really in common other than they type of their args. I think they should not be named so similarly. * I oppose the names MAPCAR and MAPC. T calls these MAP and WALK, respectively. The generic form of MAPCAR, which is the only thing for which arbitrary order of application would make sense (since lists are only sequentially accessible anyway), has no business being called MAPCAR. * With respect to the questions about VECTOR->LIST, I think the right thing to say is that the conses it returns are mutable, not that the result is necessarily a "new object", since if the result is the empty list (eg, from an empty vector), I wouldn't want the implication to be that (NOT (EQ (VECTOR->LIST #()) (VECTOR->LIST #()))) since it follows from that that more than one false value must (rather than "may") be possible. * What happens if VECTOR-REF is out of range? * VECTOR-SET! is ugly. It should at least be called SET-VECTOR-REF! for symmetry with the other SET- things. Personally, I hate the ! and would strongly prefer just SET-VECTOR-REF. * The relation between OBJECT-HASH and the GC should be specified. Do things get GC'd if no other pointers exist to them? Also, it might help to distinguish this kind of "hash" from the number that comes from SXHASH in Maclisp. It took me a second to realize you weren't talking about that.  Received: by YALE-BULLDOG.YALE.ARPA; 4 Dec 84 11:49:28 EST (Tue) Message-Id: <8412041649.AA04845@YALE-BULLDOG.YALE.ARPA> Received: from YALE-RING by YALE-RES via CHAOS; Tue, 4 Dec 84 11:37:43 EST Subject: Re: Kent Dybvig's Comments on Scheme Date: Tue, 4 Dec 84 11:37:46 EST From: Paul Hudak To: Bill Rozas Cc: Scheme@MIT-MC, Hudak@YALE.ARPA In-Reply-To: Bill Rozas , 30 Nov 1984 23:35 EST (Fri) Not having participated in the workshop, I've been hesistant about making comments on the various issues flying by recently, but I couldn't resist responding to Bill Roza's comments on special forms: I think that there are only two valid reasons for adding special forms: - They provide a convenient syntax for something which could be expressed only considerably more clumsily without them. Example: COND (as compared to nested IFs). These are usually macros which expand into the clumsier form. - They provide an extension to the language which requires special handling by the interpreter or compiler. Their effect could otherwise not be achieved. Example: QUOTE. These are the "TRUE" special forms. I think there's one other reason, which in a sense subsumes the first: *readability*. In particular, with regard to IF having or not having an alternative sub-expression, I find that when I'm reading code and come across an IF expression, I look very hard to see if it has an alternative branch. If it doesn't, I look again to be sure I didn't miss it. I think this "convenient feature" degrades readability. I agree with Bill Rozas that one shouldn't proliferate special forms, but my reason is that they are typically complex, and their syntax becomes hard to remember. However you can't get much simpler than WHEN and UNLESS. Their use, in my opinion, greatly enhances readability. - Paul Hudak (hudak@yale)  Date: 30 Nov 1984 23:35 EST (Fri) Message-ID: From: Bill Rozas To: David Bartley Cc: Scheme@MIT-MC.ARPA Subject: Kent Dybvig's Comments on Scheme In-reply-to: Msg of 30 Nov 1984 12:47-EST from David Bartley BEGIN should certainly not add another contour. BEGIN is equivalent to LET() if environments are not first class, but they are in MIT Scheme. An incremental definition to the environment where the subexpressions of the BEGIN expression are evaluated would have different effects depending on whether a new contour was created or not. I agree that BEGIN (for lack of a better name) should indicate a compound expression and LET should be used for blocks. Note that this is not contrary to all early Scheme papers since in the Revised Report, (BLOCK X Y) = ((LAMBDA (A B) (B)) X (LAMBDA () Y)). X is evaluated in the environment where the BEGIN expression appears, and Y is evaluated in an environment where no bindings have been added. In a dialect without first-class environments and without internal definitions, the environment where Y is evaluated is the same as that where X is evaluated, thus no contours have been added. I think that there are only two valid reasons for adding special forms: - They provide a convenient syntax for something which could be expressed only considerably more clumsily without them. Example: COND (as compared to nested IFs). These are usually macros which expand into the clumsier form. - They provide an extension to the language which requires special handling by the interpreter or compiler. Their effect could otherwise not be achieved. Example: QUOTE. These are the "TRUE" special forms. I don't think that CALL-WITH-CURRENT-CONTINUATION falls in either class, so there is no need to make it a special form. Besides, a portable program would not be able to rename it since we have not specified a way for adding syntactic extensions. Allowing IF not to have an alternative sub-expression is convenient because it is clear and eliminates the need for WHEN, another special form. I don't like the proliferation of special forms and would object strongly to requiring another one for this purpose. I can't see that there is a difference between WHEN and IF in terms of compile-time checking. IF with two subexpressions and IF with three subexpressions can be treated as different beasts. I agree with the remaining three points. - Bill Rozas (JINX@MIT-MC)  Received: from ti-csl by csnet-relay.csnet id a008334; 30 Nov 84 18:12 EST Date: 30 Nov 1984 1147-CST From: David Bartley Subject: Re: Kent Dybvig's Comments on Scheme To: Scheme@mit-mc.ARPA cc: Bartley%ti-csl.csnet@csnet-relay.arpa Re: Kent Dybvig's comments on the preliminary report on Scheme I'd like to briefly add my "votes" on Kent's suggestions and bring up some additional complexities in the issues he raised. Lambda Syntax I prefer (REC name (LAMBDA args . body)) first, then MIT-style NAMED-LAMBDA second. (I support both.) Will's discussion summarizes my reasons quite well. We should NOT require all lambdas to be named. IF without else part I would not mind requiring WHEN in this case, making (IF a b) syntactically incorrect. This allows better compile-time checking. I use WHEN myself to make this distinction and find it useful. BTW, I do NOT find UNLESS useful, since (WHEN (NOT ..)..) usually reads better. BLOCK vs BEGIN vs LET() As Will points out, the real question is whether the construct we have in mind is a compound expression or an Algol-like block containing both declarations and expressions to be evaluated. I prefer BEGIN for the former and LET for the latter. The problem is that Will suggests (paraphrasing someone else) that (BEGIN ...) might be equivalent to (LET () ...). I have conflicting thoughts on this. Pro: if they are the same, then I would vote to remove BEGIN from the (essential) language as hopelessly redundant with LET. Con: early papers by Steele and Sussman treat (BEGIN a b) as a macro for ((LAMBDA (ignored-id) b) a), which is equivalent to (LET ((ignored-id a)) b). Thus, BEGIN already is a scope-extending operation. This has the following practical consequence. What is the scope of FOO in the following? (define (f ...) (define g ...) (begin (define foo ...)) body) I understand that MIT Scheme "promotes" both G and FOO directly under F. This hack is needed for macros that expand into multiple DEFINEs and thus must be "wrapped" in something to be spliced in correctly. If we define BEGIN in terms of LET, then the scope of FOO will have to be confined to the scope of the BEGIN. What should we do? I vote that BEGIN indicate a compound expression, contrary to early Steele&Sussman, and that LET() be used for Algol-style blocks. CASE expressions with single keys We will accept single keys in CASE expressions. If ELSE is to be a single key in the last clause, however, it will have to be enclosed in parentheses to avoid being interpreted as "otherwise". CALL/CC "CALL-WITH-CURRENT-CONTINUATION" is nonsense to the unititiated, too. I will support both, but feel like it's silly to have the long name. Must this be a procedure (e.g. a potential funarg) or can we treat it as a special form? Transcendental functions What's the problem? Just call them "optional" as a group. What we've been arguing is whether I have "true Scheme" if I have some kind of representation for "real numbers" but don't support a certain set of functions. This is irrational!! (sorry) Generic LENGTH I strongly abhor Common LISP-style genericity. As Will says, "generic" operations on numbers with multiple REPRESENTATIONS is one thing, but trying to be generic across different kinds of things is something else. Case-sensitive symbols I feel very strongly that symbols, as processed by the reader, should NOT be case sensitive and that (EQ? 'a 'A) be true. STRING->SYMBOL should preserve case, however, and (EQ? '|a| '|A|) should be false. -- David Bartley (Bartley @ TI-CSL) -------  Received: from indiana by csnet-relay.csnet id aa00469; 29 Nov 84 12:24 EST Received: by iuvax.UUCP (4.12/4.7) id AA27853; Wed, 28 Nov 84 23:48:00 est Date: Wed, 28 Nov 84 23:48:00 est From: Will Clinger To: dyb%UNC%indiana.csnet@csnet-relay.arpa Subject: Re: Kent Dybvig's comments (long message) Cc: scheme@mit-mc.ARPA Thank you for your comments on the preliminary draft. I see you have received responses from Bill Rothas and from Chris Hanson. My remarks below were complete in draft form before I saw their remarks. I wish you had been able to attend the workshop, because your points of view would have enriched some of the discussions. Lambda Syntax Your (lambda name formals . body) syntax is equivalent to the optional (named-lambda (name . formals) . body) syntax. As you observe, your syntax conflicts with the (lambda x ...) syntax, which together with the (lambda (x y . z) ...) syntax has been in MIT Scheme for perhaps five years, and has been used in T for three years. The named-lambda syntax has been in MIT Scheme for at least two years. The named-lambda syntax was not very controversial at the workshop, since of the implementations represented there only MIT Scheme uses it. I feel that (rec name (lambda formals . body)) is better because it is more general, but named-lambda was made an optional feature as a concession to MIT Scheme and to simple compilers that might not be able to compile the rec form as well as the named-lambda. The syntax for rest arguments was opposed vigorously by one person, who argued for (mulambda x . body) as the only way to obtain rest arguments. His proposal would have been compatible with your syntax, but he was unable to muster additional support for his position even though he argued the matter at length. One of his arguments was that destructuring was a can of worms that we shouldn't get into; apparently he was the only one at the workshop who thought of the dotted pair syntax as indicating a destructuring, however -- the rest of us viewed it as syntax pure and simple. The &rest syntax was mentioned once but no one at the workshop felt like arguing in its favor. I think most people at the workshop are repelled by the complexity of Common Lisp's formal argument lists, and don't want to have anything that even looks like them. In addition the &rest is less visible than a period because it looks so much like an identifier. As for requiring that all lambdas be named, I think you'd have about as much luck convincing people of that as you would convincing people that all integer constants should be named. Much of the beauty of functional programming derives from anonymous procedures. Other Special Forms 1. When programming imperatively, if statements without else parts are convenient. We made sure that implementations are free to perform sanity checks by declaring it a mistake to use the result of an if that doesn't have an else part (when the test evaluates false; I think that restriction was an oversight and will be changed so that it is always a mistake to use the result of such an if). 2. The use of "block" rather than "begin" conflicts with standard programming language terminology dating back to the Algol 60 report. In Algol 60, a block necessarily contains declarations at its head. The "begin" expressions of Scheme correspond to Algol 60 compound statements, not to Algol 60 blocks, and this has been a significant source of confusion. (Note that even Pascal blocks, which do not necessarily contain declarations, are distinct from Pascal compound statements.) At the workshop there was a consensus that "block" had to go. The main suggestions to replace it were "progn" and "begin", neither of which was judged particularly wonderful. The argument in favor of "progn" was tradition; the argument against "progn" was that it was utterly and irredeemably random. Several votes were taken comparing the various candidates head to head with other candidates, asking how many people could live with the various candidates, and so on. "begin" was the winner. It was at one point remarked that (let() ...) was equivalent to (begin ...) and required the same number of keystrokes. We took this seriously but decided to have a special form for it anyway. 3. Many implementations will allow single keys in case expressions, as an extended feature. Several people wanted the single key idea to be optional rather than extended, but there were objections and the feature didn't seem important enough to justify a long discussion. Please remember that complete unanimity was required to adopt even optional features. Procedures 1. "call/cc" is nonsense to the uninitiated, and even "call-with-current-continuation" is a bit random. As for the number of keystrokes, most of us felt that it was appropriate to discourage people from using call-with-current-continuation so often that their fingers wear out; if there is a common need for it in a program, the programmer should encapsulate it in his/her own routine, with as short a name as he/she desires. In short, if you prefer call/cc you can simply say (define call/cc call-with-current-continuation) and forget about it. 2. Your point about transcendental functions is well taken. There was a consensus that the transcendental functions should be essential, but two of us argued the irrelevance of transcendental functions to certain kinds of systems programming. The wording in the preliminary report was deliberate -- I wanted to avoid the problem that Common Lisp has, in which the implementation strategies are dictated in large part by the language definition. The workshop's decision was worded as you want, however -- "implementations with floats must support" the transcendentals. I don't know how to say that without talking about implementation strategies, so I said it the way I said it. I'm very open to suggestions here. 3. I disagree about the length function. It seems that the programmer almost always knows the type of the object whose length is to be calculated, so a generic length function doesn't seem that helpful. The real question is whether everything should be as generic as possible. I think the workshop participants felt that Common Lisp had gone overboard in the direction of genericity, and didn't want to follow; once you get started on genericity it's hard to stop, so we didn't start. One exception has to do with numbers. The rationale is that the programmer wants real numbers -- of which the integers are a subset -- but has to settle for rational numbers. What is an integer but a rational whose denominator divides the numerator? What is a floating point number but a rational with the additional information that it probably isn't very accurate? With vectors, strings, and lists, however, the programmer wants the distinctions, because they are conceptually different data structures. I suspect that this issue will continue to come up, but my current feeling is that we're doing it right. Anyone that wants a generic length-of procedure can write it themselves; if we see a lot of programmers doing that, we'll move it into the manual, but not before. Lexical Matters I prefer case-sensitive symbols myself, but I have to agree with the position of the workshop that when case is important the programmer probably should be using strings instead. I don't follow your argument about Prolog-like variables at all -- why should they be difficult to implement in Scheme? In some languages case is important but not in others; all this means is that you can't use the same lexical analyzer for all languages. Some people will be implementing Common Lisp on top of Scheme, but there's little hope that the same parser can be used both for Common Lisp and for Scheme, let alone for both APL and Scheme. I had forgotten that some terminals work only with upper case. That certainly was not a consideration at the workshop. The string->symbol procedure doesn't change case; the preliminary report did not say so explicitly, but it should have. Peace, Will  Received: from unc by csnet-relay.csnet id a003506; 29 Nov 84 5:49 EST Received: by unc (4.12/4.7) id AA09766; Thu, 29 Nov 84 00:38:00 est Date: Thu, 29 Nov 84 00:38:00 est From: Kent Dybvig Message-Id: <8411290538.AA09766@unc> To: JINX%MIT-OZ@mit-mc.ARPA Subject: Re: Comments on the Preliminary Report Cc: bts%unc.csnet@csnet-relay.arpa, scheme@mit-mc.ARPA Lambda: I don't wonder that "named-lambda" is seldom used explicitly in code. The syntax is too clumsy. What I'm looking for is a way to add a name to any lambda expression, without changing the expression fundamentally (like giving it a new name). I think people should be encouraged to name their functions in this manner since it produces more robust, efficient, and readable code. Named lambda isn't likely to be used much in mit-scheme anyway because of the "(define (foo ...) ...)" syntax. For those of us who wish to see our lambdas explicit, named lambda is quite convenient. List-length: If we don't have a generic length function, we should call the function that returns the length of a list "list-length". This would be consistent with the names of other length functions and leave the name length open for future genericization. Case sensitivity: I agree that having two different functions Length and length would be confusing. Why not make lexical identifier resolution case insensitive but have a way to tell apart two symbols that differ only in case? (let ((x 3)) X) => 3 (eq 'x 'X) => #!false (eqv 'x 'X) => #!true (string->list (symbol->string 'X)) => (#\X) (string->List (symbol->string 'x)) => (#\x)  Received: from indiana by csnet-relay.csnet id ab01067; 28 Nov 84 19:20 EST Received: by iuvax.UUCP (4.12/4.7) id AA24449; Wed, 28 Nov 84 18:23:09 est Date: Wed, 28 Nov 84 18:23:09 est From: Eugene Kohlbecker To: scheme%indiana.csnet@csnet-relay.arpa Subject: Signing your name Today we (at IU) received three postings from the scheme network. Unfortunately, because either our local mail thingy or the MIT one that sends us stuff strips information such as who wrote the note, we can only infer authorship. And unless there's textual content to indicate source, we can't even tell at what site the note originated. I think I know who two of the authors are (Kent Dybvig and Bill Rozas), but I don't know about the third one. I'm asking our local people to see if the fault's ours. But, in the meantime, please sign your name when you post. Thanks, Eugene Kohlbecker  Date: Wed, 28 Nov 1984 14:51 EST Message-ID: From: CPH%MIT-OZ@MIT-MC.ARPA To: Bill Rozas Cc: Kent Dybvig , scheme@MIT-MC.ARPA, willc%indiana.csnet@CSNET-RELAY.ARPA Subject: Comments on the Preliminary Report In-reply-to: Msg of 28 Nov 1984 13:51-EST from Bill Rozas As I recall, the stated reason for preferring CALL-WITH-CURRENT-CONTINUATION over CALL/CC was both for clarity, and, precisely because it is much harder to type. It was felt that making it incredibly hard to use would prevent it from being overused. If only the designers of the Flavor system had possessed such insight!  Date: 28 Nov 1984 13:51 EST (Wed) Message-ID: From: Bill Rozas To: Kent Dybvig Cc: scheme@MIT-MC.ARPA, willc%indiana.csnet@CSNET-RELAY.ARPA Subject: Comments on the Preliminary Report In-reply-to: Msg of 27 Nov 1984 23:00-EST from Kent Dybvig A) Special forms: 1) In our system (MIT-Scheme) named lambdas can be obtained by using the special form NAMED-LAMBDA with the following syntax: (named-lambda ) where is a pair whose car is the name and whose cdr is a normal formal parameter specification for a lambda. Thus (NAMED-LAMBDA (FOO . ALL) ) evaluates to a procedure internally named FOO, which accepts any number of arguments which will be collected into a list named ALL. In the internals of the implementation all lambdas are named. The special form LAMBDA is actually a macro which expands into NAMED-LAMBDA with an unreadable name. This syntax matches the syntax we use for definitions of procedures, and the DEFINE macro expands the implicit lambda into a NAMED-LAMBDA: (DEFINE (FOO ARG) ) = (DEFINE FOO (NAMED-LAMBDA (FOO ARG) )) Named lambda appears explicitely only rarely in our code. I therefore feel that forcing all lambdas to be named by the programmer is very inconvenient and not justified by need. I think that we should not modify the syntax for LAMBDA, and optionally add the special form NAMED-LAMBDA with the syntax specified above. 2) I don't like BEGIN myself and voted against it, but I don't think BLOCK is a good name either. The special form LET corresponds more closely to a block in a block-structured language than BEGIN does. I would much rather use a name which indicates the sequentiality and therefore the meaning of the special form, but BEGIN was found to be the least unacceptable of the alternatives. B) Procedures: 1) The default name of a procedure should be meaningful and is otherwise irrelevant. I don't think that call/cc is a good default name since it is not meaningful. I agree that call-with-current-continuation is very long, but anyone who is really attached to any other name (I like catch, which is just as inappropriate) can always define it to be the value of call-with-current-continuation. The names of special forms are terribly important, however, since we have not agreed on a way for defining new ones. 3) While making the length procedure be generic may be reasonable, I think we should not go the Common Lisp way and make everything generic until we come up with an efficient and elegant way for incrementally extending the domain of procedures. The same argument that would make length generic would make first (car) and rest (cdr) generic, but this is too inefficient. Choosing only some data-structure functions to be generic and not others would be inconsistent. C) Lexical matters: I disagree. It is terribly confusing to have Length and length functions that do different things. Uninterned symbols or strings can be used when case matters. When implementing a Prolog in Scheme, a new parser must be written. This parser can easily avoid the case-insensitivity of Scheme by not canonicalizing the case before interning the symbols. An entirely different matter is which way case is canonicalized. I think that Scheme parsers should coerce to lowercase before interning.  Received: from unc by csnet-relay.csnet id a021839; 28 Nov 84 6:30 EST Received: by unc (4.12/4.7) id AA17493; Tue, 27 Nov 84 23:00:58 est Date: Tue, 27 Nov 84 23:00:58 est From: Kent Dybvig Message-Id: <8411280400.AA17493@unc> To: scheme@mit-mc.ARPA Subject: Comments on the Preliminary Report (2 pages) Cc: willc%indiana.csnet@csnet-relay.arpa Comments on Essential Scheme These comments are arranged in four categories: lambda syntax, other special forms, functions, and lexical matters. I feel quite strongly about the issues I outline here. I have other minor gripes (don't we all) that I didn't bother to include here, for fear they would water down the substantive issues. Lambda Syntax For several years my Scheme system has had a syntax for lambda expressions that allows naming of the closure within the body of the closure for enhanced readability and efficiency. The syntax is: (lambda name formals . body) where name is a symbol bound lexically to the closure within body, formals is a list of formal parameters, and body is a set of zero or more forms to execute. Name may be omitted and there is no ambiguity since formals is always a list. This feature, which I have called "named lambda," allows terse, referentially transparent recursive function definitions. It is similar to the optional "rec," special form, but is shorter and perhaps more easily optimized than rec. It is quite a bit shorter than using "letrec." For example, the boring factorial function looks like: (lambda fact (x) (if (= x 0) 1 (* x (fact (- x 1))))), a totally self contained definition. Named lambda generalizes to named let, something which seems to have appeared elsewhere independently. It looks like: (let name ((x1 v1) (x2 v2) ...) . body), where name may again be omitted. It translates into: ((lambda name (x1 x2 ...) . body) v1 v2 ...), and replaces the old iterate expression. Unfortunately, the partial destructuring provided by the adopted lambda syntax conflicts with the named lambda syntax, introducing an ambiguity in some cases, as in: (lambda f (g x) ...). Is this a named lambda with two formals or an unnamed lambda with a single &rest formal? The partial destructuring syntax is merely a different syntax for the &rest formal parameter adopted by Common Lisp. The only benefit to be gained by not using the Common Lisp syntax is that some implementations might generalize to full destructuring. I think it more likely that we would want to generalize to optional arguments. Why not just use part of the syntax adopted by Common Lisp? For now, we could make the &rest syntax required and the &optional syntax optional. If everyone dislikes the &rest syntax then I think we ought to resolve the ambiguity with named lambda by requiring the name to be present. This would improve code readability and facilitate optimization regardless of the system the code is executed on. Other Special Forms 1. The "if" special form should require an else part. This allows the compiler/interpreter to make a sanity check for the user which might save some grief, and seems a little cleaner. The macros "when" and "unless" are the appropriate way to express the intended meaning. 2. Block is a much better name for the statement grouping special form than "begin". Do we really need an artifact of Algol syntax in our language? After all, it is a block of code, not a begin of code. (If someone is worried about clashing with a process blocking function they should name their function "block-process.") 3. Case expressions should allow single keys without putting them in a list. The user rarely wants the single key to be a list (eq semantics and all), so there really isn't any ambiguity. Since single keys are probably the most common, case expressions will be less bulky. For example, (case x ((a) ...) ((b) ...) ((c d) ...) (else ...)) would simply be (case x (a ...) (b ...) ((c d) ...) (else ...)). Functions 1. Call/cc should be an alternative to call-with-current-continuation. How are we ever going to get people to feel comfortable with something so imposing that it requires 30 keystrokes to type in and takes up half a line? 2. Transcendental functions should be required only in implementations with floating point numbers, not just any implementation with numbers other than integers. In particular, an implementation with rational or interval arithmetic should not be bound to supporting transcendental functions. (This was probably just a misstatement in Will's note.) 3. The length function should be generic, returning a value for all reasonable arguments. There can still be individual functions list- length, string-length, vector-length, and so on. Lexical Matters Scheme systems should be case sensitive. Let's not forget that symbols have other uses than as Scheme identifiers. How can we implement prolog-like variables in Scheme without being able to differentiate between upper and lower case letters within the symbols? Does anyone really still use an upper-case only terminal? Assuming case sensitivity, all standard Scheme functions and special forms should be written with entirely lower-case letters.  Received: from indiana by csnet-relay.csnet id ad04907; 11 Nov 84 23:38 EST Date: Sun, 11 Nov 84 21:56:06 est From: Will Clinger Message-Id: <8411120256.AA05811@iuvax.UUCP> To: scheme@mit-mc.ARPA Subject: preliminary report of workshop Greater standardization of the Scheme programming language was the object of a workshop held at Brandeis University on 22-23 October 1984. The participants were Hal Abelson (MIT), Norman Adams (Yale), David Bartley (TI), Gary Brooks (TI, IU), William Clinger (IU), Dan Friedman (IU), Robert Halstead (MIT), Chris Hanson (MIT), Chris Haynes (IU), Eugene Kohlbecker (IU), Don Oxley (TI), Jonathan Rees (MIT), Bill Rozas (MIT), Gerald Sussman (MIT), and Mitchell Wand (IU, Brandeis). Kent Pitman (MIT) contributed to the agenda but was unable to attend the sessions. The participants, through some compromises, were able to reach unanimous agreement on an essential core language and on the syntax and semantics of a number of optional features that most implementations will want to support. A few issues were deferred to committees: input/output, additional operations on strings, and the gruesome details of numbers and arithmetic. This preliminary report is intended for implementors that need to know the results of the workshop now in order to conform to the standards that were agreed upon. Comments are solicited as well. The final report is being written by William Clinger and is expected by 1 March 1985. ---------------------------------------------------------------- Preliminary Report of the October 1984 Workshop on Scheme Essential features must be supported by every implementation of Scheme. Programs written in Essential Scheme will run on any implementation of Scheme worthy of the name. Optional features may not be supported by every implementation, but those that do support a feature will use the same syntax and semantics for the feature. Hence code that makes use of optional features will run on any implementation of Scheme that supplies the optional features. Optional features are indicated in this preliminary report by prefixing them with "Optionally, ". An implementation may extend the language in any way whatsoever, but code that makes use of extended features is not portable. This preliminary report is divided into the following sections: lexical features special constants and variables special forms data types procedures ---------------------------------------------------------------- Lexical features The following characters are whitespace characters: The space character. The tab character. The newline character. The carriage return character. The line feed character. The form feed character. The following characters are delimiters that terminate symbols: Any whitespace character is a delimiter. Left parenthesis is a delimiter. Right parenthesis is a delimiter. Left square bracket is a delimiter. Right square bracket is a delimiter. Left curly brace is a delimiter. Right curly brace is a delimiter. Semicolon is a delimiter. Double quote is a delimiter. End of file is a delimiter. Left and right square brackets and left and right curly braces have not yet been assigned any meaning but they are reserved for future use. The period is not a delimiter that terminates symbols. Optionally, the following characters may be delimiters that terminate symbols: Single quote. Backquote. Sharp sign. It is not specified whether or not the vertical bar is a delimiter that terminates symbols. No escape character is specified for use in symbols. (There is widespread agreement that "slashification" of characters within symbols is a relic that ought to be abandoned.) Lists may be notated in the traditional way using an opening left parentheses and a closing right parentheses. Whitespace may follow the opening parenthesis and precede the closing parenthesis. The empty list may be notated as (). Conses may be notated as (x . y), where x and y are any notations for an object. Whitespace is required on each side of the period. Possibly improper lists may be notated in the traditional way as (... x . y). (foo . ()) reads the same as (foo), but (foo . nil) does not. Strings are delimited by double quotes. Double quotes may be included inside strings by escaping them with backslash characters. Backslashes may be included inside strings by escaping them with backslashes. 'x reads as (quote x) . Optionally, backquote, comma, and comma at-sign (",@") work in the traditional way to one level of nesting. (Implementations that support this feature may in fact allow arbitrary levels of nesting, but the workshop chose not to take the time to determine the right semantics for deeper levels of nesting. It appears that at least one major implementation of Lisp has gotten it wrong in the past.) Comments are opened by semicolons and closed by end of line or end of file characters. Optionally, sharp sign macros are introduced by a sharp sign. Implementations that don't have sharp sign macros must still support #!true, #!false, and the #(...) notation for vectors. The workshop chose not to specify a notation for unreadable objects. (Hal Abelson suggested that the machine should pronounce them rather than print them.) Vectors may be written using #(...) notation. The default input radix is decimal. Optionally, binary numbers may be written using the #b notation. Optionally, octal numbers may be written using the #o notation. Optionally, decimal numbers may be written using the #d notation. Optionally, hexadecimal numbers may be written using the #x notation. Optionally, special characters may be written using the #\ notation. If this feature is supported, then the Common Lisp names for special characters must be supported. #!true reads as a true object, and #!false reads as a false object. Integers may be written in the usual way, with or without signs. Optionally, numbers may be written using decimal points and/or exponents. The precise notations and semantics were deferred to the numbers committee. Symbols may be written as sequences of characters that contain no special characters and begin with a character that cannot begin a number. (This is not to imply that there are no other symbols; in particular +, -, *, /, 1+, and -1+ should be symbols.) The precise language for symbols is not otherwise specified. When the 26 letters of the alphabet are used to write a symbol, case is immaterial unless overridden by slashification. (This is not to say that slashification is an essential or optional feature, but rather to allow for implementations that have slashification as an extended feature.) ---------------------------------------------------------------- Special variables and constants The empty list counts as false in conditional expressions. (There may be other false values as well.) The empty list is not the same as the symbol NIL. (This is incompatible with most other Lisps.) #!false reads as a false value that evaluates to itself. #!true reads as a true value that evaluates to itself. Case is immaterial. Optionally, #!null reads as the empty list. Case is immaterial. Optionally, the symbol NIL evaluates to the empty list. Optionally, NIL evaluates to some false value. Optionally, T evaluates to some true value. ---------------------------------------------------------------- Special forms The order of evaluation within an application is not specified. (QUOTE object) evaluates to object. (LAMBDA varspec expr ...) evaluates to a procedure. varspec may be a proper list of symbols, a symbol, or an improper list of symbols. The body of the LAMBDA is an implicit BEGIN. The semantics associated with the various possibilities for varspec are as described in the MIT Scheme manual and the T manual. (LET ((id1 expr1) ...) expr ...) is equivalent to ((LAMBDA (id1 ...) expr ...) expr1 ...) (LETREC ((i1 expr1) ...) expr ...) binds i1... to fresh locations holding unspecified values, evaluates expr1... in some unspecified order in the resulting environment, and then evaluates expr... . The expr1... expressions do not have to be lambda expressions, but they must be evaluable without referring to any of i1... . If this restriction is violated, the effect of the LETREC is unspecified, and some implementations may signal an error. The body of the LETREC is an implicit BEGIN. (IF test consequent alternative) is an if-then-else expression as described in the Revised Report on Scheme. The alternative may be omitted when evaluation is for effect rather than for value. It is a mistake for a program to make use of the value returned by an (IF test consequent) expression, and some implementations may signal an error if the value is used when test evaluates false. [Should implementations be allowed to signal an error when the value of (IF test consequent) is used and test evaluates true?] (COND clause1 ...) is similar to the COND expressions of Common Lisp. Each clause is a list of one or more forms, and the semantics is as described by cases below: (COND (form) ...) is equivalent to (OR form (COND ...)) where OR is the optional special form described below. (COND (form1 form2 ...) ...) is equivalent to (IF form1 (BEGIN form2 ...) (COND ...)) (COND) returns an unspecified value; it is a mistake to use that value, and some implementations may signal an error if it is used. Also, in each implementation either ELSE must be a standard variable whose value is initially true or else (COND (ELSE form2 ...)) must be equivalent to (BEGIN form2 ...) . (SET! id expr) evaluates expr and stores the result in the location denoted by the identifier id. If id is does not denote a location, as would happen for example if id were unbound, then the effect of the SET! is not specified, and some implementations may signal an error. SET! forms should be evaluated for effect, for they return an unspecified value; it is a mistake for a program to use the value returned by a SET! expression, and some implementations may signal an error if it is used. (DEFINE id expr) allows top-level definitions of variables. Its semantics at top level are similar to the semantics of (SET! id expr). The difference is that if id is not already bound to a location, then the DEFINE form binds id before performing the assignment, whereas it would be a mistake to perform a SET! on an unbound identifier. The value returned by a DEFINE form is not specified. (BEGIN form1 ...) evaluates form1... sequentially, returning the value of the last form. There should be at least one form in the body of the BEGIN. Optionally, (LET name ((id1 expr1) ...) expr ...) is like the LET described earlier except that it also binds name to the procedure resulting from (LAMBDA (id1 ...) expr ...) . This optional feature supersedes the ITERATE special form described in the Revised Report on Scheme. Optionally, (LET* ((id1 expr1) ...) expr ...) is as described in the T manual. Optionally, (REC id expr) is equivalent to (LETREC ((id expr)) id). Optionally, (NAMED-LAMBDA (name var1 ...) expr ...) is equivalent to (REC name (LAMBDA (var1 ...) expr ...)) where REC is as above. (Some implementations may provide better debugging information when the NAMED-LAMBDA form is used, however.) Optionally, the DO special form is as in Common Lisp, except that it does not bind the identifier RETURN. [The final report will give a more careful account of the syntax and semantics of DO.] Optionally, (COND (form1 => form2) ...) is equivalent to (LET ((form1_result form1) (thunk2 (lambda () form2)) (thunk3 (lambda () (COND ...)))) (IF form1_result ((thunk2) form1_result) (thunk3))) Optionally, the CASE special form performs a dispatch. In (CASE expr0 (selector1 form1 ...) (selector2 form2 ...) ... (ELSE else_form1 ...)) each selector should be a list of values. The selectors are not evaluated. When the CASE expression is evaluated, expr0 is evaluated and its result is compared against successive selectors using the MEMV procedure until a match is found. When a match is found the forms associated with the matching selector are evaluated in an implicit BEGIN and the result of that BEGIN is returned as the value of the entire CASE expression. If none of the selectors yield a match, then the else_forms are evaluated in an implicit BEGIN and the result returned as the value of the CASE expression. The ELSE clause may also be omitted, in which case the value of the CASE expression is unspecified when none of the selectors match; it is a mistake to use the result of the CASE expression when that happens, and some implementations may signal an error if the result is used. Optionally, the special form (AND form1 ...) is as described in the Revised Report on Scheme. (This special form goes by the name of CONJUNCTION in the Abelson & Sussman book.) Optionally, the special form (OR form1 ...) is as described in the Revised Report on Scheme. (This special form goes by the name of DISJUNCTION in the Abelson & Sussman book.) Optionally, DEFINE may be used for internal definitions as in MIT Scheme and in the book by Abelson and Sussman. If allowed at all, internal definitions are permitted only in the bodies of LAMBDA, LET, LETREC, and similar binding constructs. Furthermore they must precede the rest of the body. With these restrictions, the semantics of internal definitions can be explained in terms of LETREC. For example, (LAMBDA (x) (DEFINE foo ...) (DEFINE bar ...) ...) is equivalent to (LAMBDA (x) (LETREC ((foo ...) (bar ...)) ...)) Also (LAMBDA (...) (BEGIN (DEFINE ...) (DEFINE ...)) ...) must be treated the same as (LAMBDA (...) (DEFINE ...) (DEFINE ...) ...) and so on. Optionally, DEFINE forms may permit MIT's extended syntax in which (DEFINE (foo ...) ...) is equivalent to (DEFINE foo (REC foo (LAMBDA (...) ...))) where REC is the optional special form described above, and (DEFINE ((foo ...) ...) ...) is equivalent to (DEFINE (foo ...) (LAMBDA (...) ...)), and so on. Optionally, (DEFINE! id expr) is equivalent to (DEFINE id expr) when typed at the top level. Within code, (DEFINE! id expr) is equivalent to (SET! id expr) unless id is unbound, in which case the DEFINE! form creates a new top level binding for id before performing the assignment. The value returned by a DEFINE! form is not specified, and it is a mistake to use it. Optionally, (DEFREC! id expr) is equivalent to (DEFINE! id (REC id expr)). Optionally, SEQUENCE is synonymous with BEGIN. (SEQUENCE is for compatibility with the Abelson and Sussman text, and should not be used in new code.) ---------------------------------------------------------------- Data types All Scheme objects have unlimited extent. (The workshop did not require that any data types be disjoint. In theory, therefore, strings could be indistinguishable from lists of numbers, and would therefore be printed as lists of numbers by the standard printer. Until we can agree on a better standard, we have to rely on the good judgement of implementors to avoid such problems.) There is an object that represents both false and the empty list. Procedures are a kind of object. Numbers are a kind of object. (So far, every essential or optional operation on numbers is generic, although some operations have domain restrictions.) Symbols are a kind of object. Pairs are a kind of object. Pairs are heterogenous mutable records with two fields, known as the CAR and CDR fields. Characters are a kind of object, but they need not be distinguishable from certain other kinds of objects. (This is to allow characters to be implemented as small integers or as strings of a single character.) Strings are a kind of object. Vectors are a kind of object. Vectors are simple heterogenous mutable structures indexed by integers, with 0 as the least index. [The question of whether streams should be a kind of object was deferred to the I/O committee.] ---------------------------------------------------------------- Procedures The unary predicate NULL? is true of the empty list and false of everything else. The unary procedure NOT returns a true value if its argument is false and a false value if its argument is true. The binary procedure APPLY takes a procedure f and a list l, and returns the result of f applied to l (considered as a list of arguments.) Optionally, APPLY may be extended as described in the T manual. The unary procedure CALL-WITH-CURRENT-CONTINUATION takes a procedure f, and applies f to an escape procedure of one argument corresponding to the current continuation. The escape procedure has unlimited extent. The unary predicate NUMBER? is true of numbers and false of everything else. The unary predicate INTEGER? is true of integers and false of everything else. Every integer is a number. (For the sake of stripped-down Scheme implementations intended for systems programming, it is not essential that there be numbers other than integers. It is expected, however, that almost all implementations will approximate the behavior of real numbers as well as does the IEEE 32-bit floating-point standard, and many implementations will do better through the use of bignums, ratios, and whatnot.) The binary procedures +, -, *, and / take two numbers and return the sum, difference, product, and quotient of their arguments, or at least the best approximation thereto that an implementation can reasonably provide. (Thus (/ 2 9) may return a ratio in one implementation, a floating point number in another, and may even return 0 in a minimal implementation that approximates all numbers by integers.) Optionally, they may be generalized to arbitrary arity as in Common Lisp. (Hence unary negation is optional.) Optionally, the unary procedures 1+ and -1+ take a number and return its successor and predecessor, where the successor of x is defined as x + 1 and the predecessor as x - 1. (The names make sense if read as infix notation.) The binary procedures QUOTIENT and REMAINDER take two integers and return the quotient and remainder. The second argument must not be zero. Precisely: if the two arguments are x and y, and x = qy + r where q and r are integers, ry >= 0, and 0 <= abs(r) < abs(y), then the quotient is q and the remainder is r. [Did I get this right?] Optionally, the binary procedure MOD is as in Common Lisp. [The numbers committee should confirm this.] The unary procedure ABS takes a number and returns its absolute value. Optionally, the binary procedure GCD takes two integers and returns their greatest common divisor. [Can one of the two arguments be zero? Can the arguments be negative? Is the result always positive?] The binary procedures MIN and MAX take two numbers and return the minimum and maximum. Optionally, they may be generalized to arity >= 1. The unary procedure RANDOM takes a positive integer n and returns a nonnegative integer less than n. (The intent is that RANDOM should implement a random number generator using a high quality algorithm.) The binary predicates =, =?, <, , >?, >=, >=?, <=, and <=? take two numbers and return true iff the first argument bears the named relationship to the second. (Despite the convention that the names of true predicates end with a question mark, some people prefer these names without the question mark, hence the redundant names.) The unary predicates ZERO?, NEGATIVE?, and POSITIVE? take a number and return true if the argument is zero, negative, and positive. EXPT takes two numbers x and y and returns x raised to the y-th power. Optionally, there exist numbers that are not integers. (Almost all implementations will support this option!) If there are numbers other than integers, then the following procedures must be supported: (EXP x) returns e to the x-th power. (LOG x) returns the natural logarithm of x. (SQRT x) returns the square root of x. (SIN x) returns the sine of x, where x is in radians. (COS x) returns the cosine of x, where x is in radians. (TAN x) returns the tangent of x, where x is in radians. (ASIN x) returns the arcsine of x in radians. (ACOS x) returns the arccosine of x in radians. (ATAN y x) returns the arctangent of y/x in radians. (FLOOR x) returns the largest integer <= x. (CEILING x) returns the least integer >= x. (TRUNCATE x) returns the integer of the same sign as x whose magnitude is greatest among all such integers with magnitude less than the magnitude of x. (ROUND x) returns the integer nearest to x. [Stable rounding?] [The details of the above operations on numbers are to be formulated by the numbers committee. We expect to conform with Common Lisp and APL in most respects.] The binary predicate EQ? is the most discriminating of the standard equivalence predicates. It is true iff its arguments are "iso-ontic". That is, if there is any way at all for a user to distinguish the two arguments, then EQ? will return false. This specification would allow EQ? to return false in all cases, however, so there is one more condition: if x is a bound variable, then (EQ? x x) returns true. The binary predicate EQV? is a slightly less discriminating equivalence predicate that abstracts away from some of the implementation idiosyncrasies associated with numbers. It is true iff its arguments are EQ? or both arguments are numbers having the same numeric value. (It is a mistake to ask if two numbers are EQV? when one of the numbers has lost precision through approximation, however, and there is no telling what the result will be in such a case.) [EQV? is incompatible with T and MIT Scheme because (EQV? "abc" "abc") can be false. Was that intended?] The binary predicate EQUAL? is the least discriminating of the standard equivalence predicates. It is true of its arguments x and y iff its arguments are EQV? or both x and y are strings and are STRING-EQUAL? or both x and y are pairs and their CARs and CDRs are EQUAL? or both x and y are vectors of the same length and they are EQUAL? element-wise or both x and y are of some other type and are EQUAL? component-wise in some reasonable sense. EQUAL? may not terminate on some arguments. (EQUAL? is a blunderbuss of a predicate and should not be used when a less liberal predicate will suffice.) The unary predicate SYMBOL? is true iff its argument is a symbol. The unary procedure SYMBOL->STRING takes a symbol as argument and returns its name as a string. The unary procedure STRING->SYMBOL takes a string as argument and returns a symbol having the string as its name. (The result of STRING->SYMBOL is an interned symbol, which is the only kind of symbol required by this report.) [This report defines no destructive operations on symbols or strings, so there is now no compelling reason to distinguish between a string and a copy thereof.] Optionally, the unary procedure STRING->UNINTERNED-SYMBOL takes a string and returns an uninterned symbol having the string as its name. The unary procedure PAIR? returns true iff its argument is a pair. (Scheme has traditionally had a predicate known as ATOM?, but this predicate has been superseded by PAIR?) [Pairs, formerly known as conses or dotted pairs, may be indistinguishable from vectors of length 2.] The binary procedure CONS creates a pair whose CAR field is initialized to the first argument and whose CDR field is initialized to the second argument. It is guaranteed that the new pair is not EQ? to any existing object. The n-ary procedure LIST returns a freshly consed list of its arguments. The unary procedures CAR and CDR take a pair and return the contents of its CAR or CDR field. It is a mistake to apply CAR or CDR to any object other than a pair, and some implementations will signal an error if the mistake is made. [This is incompatible with many if not most Lisps.] The binary procedures SET-CAR! and SET-CDR! take a pair x as first argument and an object y as second argument, and store y in the CAR or CDR field of x. The value returned by SET-CAR! and SET-CDR! is unspecified, and it is a mistake to use it; some implementations may signal an error if the value is used. [This also is incompatible with many if not most Lisps.] The unary procedures CAAR CADR CDAR CDDR CAAAR CAADR CADAR CADDR CDAAR CDADR CDDAR CDDDR CAAAAR CAAADR CAADAR CAADDR CADAAR CADADR CADDAR CADDDR CDAAAR CDAADR CDADAR CDADDR CDDAAR CDDADR CDDDAR CDDDDR are compositions of CAR and CDR, where for example CADDR is (LAMBDA (X) (CAR (CDR (CDR X)))). The following descriptions use the notion of a proper list. The set of proper lists is the smallest set satisfying: the empty list is a proper list a pair x whose CDR is a proper list is a proper list, provided (MEMQ x (CDR x)) is false. The unary procedure LENGTH takes a proper list as argument, and returns its length, defined as the least integer k such that CDR composed with itself k times and applied to the argument yields the empty list. The binary procedure APPEND takes two proper lists as arguments. It could be defined by (DEFINE APPEND (LAMBDA (X Y) (IF (NULL? X) Y (CONS (CAR X) (APPEND (CDR X) Y))))) but while APPEND will return a result that is EQUAL? to the result returned by the procedure coded above, and will do so without side effects, there is no guarantee that APPEND will create any new pairs. (For example, if either argument to APPEND is the empty list, then APPEND may simply return the other argument without copying it. For that matter, APPEND may copy both arguments.) Optionally, APPEND may be generalized to arbitrary arity. Optionally, APPEND! is like APPEND except that it may side effect either or both of its arguments. Optionally, REVERSE takes a proper list as argument and returns a proper list containing the elements of the argument in reverse order. REVERSE could be defined by (DEFINE REVERSE (LETREC ((LOOP (LAMBDA (X Y) (IF (NULL? X) Y (LOOP (CDR X) (CONS (CAR X) Y)))))) (LAMBDA (X) (LOOP X '())))) but while REVERSE will return a result that is EQUAL? to the result returned by the procedure coded above, and will do so without side effects, there is no guarantee that REVERSE will create any new pairs. (There is no need to create new pairs when the argument to REVERSE has length 1, for example.) Optionally, LIST-REF and LIST-TAIL take a list and a nonnegative integer as arguments, and return the values returned by the procedures defined below: (DEFINE LIST-REF (LAMBDA (X N) (CAR (LIST-TAIL X N)))) (DEFINE LIST-TAIL (LAMBDA (X N) (IF (ZERO? N) X (LIST-TAIL (CDR X) (- N 1))))) Optionally, LAST-PAIR takes a non-empty list and returns the value returned by the procedure defined below: (DEFINE LAST-PAIR (LAMBDA (X) (IF (PAIR? (CDR X)) (LAST-PAIR (CDR X)) X))) The binary procedures MEMQ, MEMV, and MEMBER take an object x and a proper list y and return the first sub-list of y beginning with an object that is EQ?, EQV?, or EQUAL? (respectively) to x. If no such element of y exists, then MEMQ, MEMV, and MEMBER return false. (This is slightly incompatible with most Lisps, which return the empty list instead of false; for now, of course, the empty list must be false in Scheme.) (The reason the names of these procedures do not end with question marks is that they return useful values rather than just true or false.) The binary procedures ASSQ, ASSV, and ASSOC take an object x and a proper list of pairs y and return the first element of y whose CAR is EQ?, EQV?, or EQUAL? (respectively) to x. If no such element of y exists, the ASSQ, ASSV, and ASSOC return false. (See notes for MEMQ, MEMV, and MEMBER above.) The binary procedure MAPCAR takes a unary procedure and a proper list and returns a list of the results obtained from applying the procedure element-wise to the proper list. (No order of evaluation is specified.) Optionally, MAPCAR may be generalized to >= 2 arguments. The binary procedure MAPC takes a unary procedure f and a proper list x and applies f element-wise to x, in order from the first element of x to the last. The value returned by MAPC is not specified, and it is a mistake to use it; some implementations may signal an error if it is used. The string procedures below have no side effects and are always sensitive to case. No collating sequence whatsoever is specified. The unary predicate STRING? is true iff its argument is a string. The binary predicate STRING-EQUAL? takes two strings and returns true iff the strings are the same length and have the same characters in the same positions. The binary predicate STRING-LESS? is an irreflexive total ordering on strings. The binary procedure STRING-APPEND takes two strings and returns the catenation of the two strings. Optionally, STRING-APPEND may be generalized to arbitrary arity. The unary procedure LIST->STRING takes a list of characters and returns a string composed of those characters in order. The unary procedure STRING->LIST is a two-sided inverse to LIST->STRING so far as STRING-EQUAL? is concerned. The unary procedure STRING-LENGTH takes a string s and returns (LENGTH (STRING->LIST s)). The binary procedure STRING-REF takes a string s and a nonnegative integer n and returns (LIST-REF (STRING->LIST s) n), where LIST-REF is the optional procedure described above. The unary predicate VECTOR? is true iff its argument is a vector. The unary procedure MAKE-VECTOR takes a nonnegative integer n as argument and returns an uninitialized vector of length n. Optionally, MAKE-VECTOR can be a binary procedure as well, in which case the second argument is an object to be stored in every element of the newly created vector. The n-ary procedure VECTOR returns a vector of its arguments, by analogy with LIST. The unary procedure LIST->VECTOR takes a proper list and returns a vector whose elements are the elements of the list. The unary procedure VECTOR->LIST takes a vector and returns a proper list of its elements. [Should the result of either be guaranteed to be a new object? What about vectors of length 0?] The binary procedure VECTOR-REF takes a vector v and a nonnegative integer n and returns element n of v, where the first element of v is element 0. The ternary procedure VECTOR-SET! takes a vector v, a nonnegative integer n, and an object x, and stores x in element n of v. The value returned by VECTOR-SET! is not specified. It is a mistake to use it, and some implementations may signal an error if it is used. The unary procedure VECTOR-LENGTH takes a vector v and returns the number of elements in v, which is one greater than the largest index acceptable to VECTOR-REF and VECTOR-SET! for v. Optionally, VECTOR-FILL! takes a vector v and an object x and stores x in every element of v. Optionally, the unary procedure OBJECT-HASH takes an object and associates an integer with that object, returning the object. OBJECT-HASH must guarantee that objects that are not EQ? are associated with distinct integers. Optionally, OBJECT-UNHASH takes an integer and returns the object associated with that integer if there is one, returning false otherwise. (OBJECT-HASH and OBJECT-UNHASH can be implemented using association lists and ASSQ, but the intent is that they be efficient hash functions for objects.)  Received: from indiana by csnet-relay.csnet id a004289; 20 Oct 84 10:32 EDT Date: Sat, 20 Oct 84 09:14:01 est From: Gary Brooks Message-Id: <8410201414.AA06193@iuvax.UUCP> To: scheme%mit-mc.arpa@csnet-relay.arpa Subject: Environments and Macros Previously, there has been a debate between Chris Haynes, David Bartley and myself (Gary Brooks) as to the nature of environment structure in Scheme and its interaction with macros. We disagree with almost all of what David Bartley said and much, if not all of what Chris Haynes said. Not only do we disagree with the principles that were advocated in their proposals, we are horrified by their complexity. Instead of analyzing each proposal on a point by point basis, we propose yet another framework that we contend is simple and straightforward, and allows one to express the type of things one wants to. Namespaces ---------- First a bit of nomenclature. The term 'namespace' has been bandied around a lot in the last few notes. But what is a namespace? As far as I can tell, it is just an association of identifiers to values, or, in other words an environment! Why use two words when one will do? Don't -- **poof** no more namespaces, just environments. The Environment Problem ----------------------- The environment problem is concerned with determining the environment in which an identifier (symbol) is to be interpreted. In the following discussion, reserved words are viewed as constituting an environment which is given priority over other environments. The crux of the problem, which can be found in Scheme84 as well as the two other proposals, is the proliferation of environments. For example, if we examine Scheme84 we find that we have a (core) reserved word environment, a global macro environment, a global primitive environment, a global environment for everything else, a lexical environment, and a fluid environment. When the environment of an identifer is not textually specified (as it is for fluids), problems arise when we try to determine the precedence of each environment in the process of interpreting an identifier. The problem of determining environment precedence is further aggrevated by making preferencing (scope rules) dependent on the position of an identifer in an application, as the two prior proposals advocated. For example, in Scheme84, the reserved word and the macro environments are given precedence for identifiers in functional position. However, the lexical and global (initial lexical) environment are given precedence in argument positions. The complicated and unintuitive scope rules resulting from position sensitive precedence is intolerable. Usually, non free identifiers are interpreted globally (i.e. in the initial lexical environment). If, while looking up identifiers, we give priority to any of the other global environments (e.g. macro) over that of lexical scope (which was advocated in previous proposals), we contaminate our notion of lexical scope. Given the elegance and simplicity of the traditional lambda calculus based lexical scoping, this type of preferencing is a "feature" we are reluctant to incorporate. In general, no matter what preferencing scheme one adopts in the presence of multiple global environments, too many rules are needed. However, if we simply abolish the proliferation of global environments and rely solely on lexical scope (augmented with an initial lexical environment, which we view as containing a binding for all possible identifiers) we can avoid the problems encountered by preferencing environments. This immediately raises the question of how macros, primitives, core items, etc. are distinguished. We proprose that they be distinguished by type (i.e. we introduce a new kind of typed object for each class of items). This emphasizes the point that the name of some entity is not important, the type of an entity is! From a compiler's point of view, determining whether an identifier is a macro, primitive, or core item is simple. The compiler need only check the type of (the binding of) each free identifier in the initial environment. Some may object to this scenario on the grounds that it allows an identifier bound to an item of one type (e.g. macros, primitives, and core items) to be redefined to be another type. In particular, some people are concerned that a crucial macro (e.g. let) may be redefined as something else (e.g. a closure). I contend that this redefinition problem is mistakenly concerned with the type of object being redefined. For example, even if the appropriate machinery prohibited the redefinition of someone's favorite macro as a closure, there would still be no conceivable way of preventing his favorite macro from being redefined as another totally random macro. Furthermore, one might reasonably want to redefine a macro as a closure to, say, effect a different space/time tradeoff. Multiple Initial Environments: ------------------------------ A related environment problem is how one accounts for the multiple "global" (initial) environments required in large scale software systems. While there is much controversy on this issue, we propose a simple framework that provides for multiple initial environments and allows for user specified (programmed) inheritance between them. The perspective that we advocate is based on the principle that the relationship between a free identifier and its L-value is determined at compile-time. We model initial environments as maps between free identifiers and L-values. The compiler interface is modified by requiring the compiler to take an additional argument, an initial environment. We point out that this scenerio allows for most inheritance schemes (e.g. packages) by simply constructing a new environment out of existing ones. However, this scenario does prohibit dynamic (i.e., runtime) resolution of free identifiers. For those who desire such capabilities, we advocate treating them in a manner similar to fluids, but not to conflate them with initial environments. For example, someone who desires to create a Unix-like shell environment for an operating system need only define the relevant macros for defining and side-effecting such "shell" variables. Binding and variable Injection ------------------------------ Lexical identifer problems arise when a lexical identifier or a lexical identifier binding is injected into an existing lexical contour within a code segment as the result of a macro expansion. Variable injection: In the first case, a macro may inject an identifier into an existing lexical contour. While this may be the desired intension, often it is not. For example, consider the following code fragment: (lambda (and) (frob a b d)) It is transformed (via the macro frob) to: (lambda (and) (and a (or b d))) Note that what is (presumably) a reference to the global macro 'and' in the definition of frob has been shadowed by the lambda variable 'and' in a user program. In such a case, we say that the injected variable 'and' is captured by an existing binding. The problem is centered on the injection of the symbol 'and' into the lambda body. Using the normal lexical/global lookup of a symbol we don't get the binding of 'and' which the macro writer intended. To see why, we must look at the way macros are defined. Consider the definition of frob: (We use typed macros as advocated above. Also, 'macro' is like lambda, but constructs a macro object instead of a closure.) (define frob (macro (x y z) `(and ,x (or ,y ,z)))) While this might look ok, it suffers from the injected variable problem. The cause of the problem is the quotation of 'and'. I contend that when the macro writer writes the macro (macro definition time), he/she expects the value of 'and' to be the value of 'and' in the macro environment (i.e. the environment when the macro 'frob' is expanded). Since, in past versions of Lisp or Scheme, macros have not been typed objects, there has been no way to do this properly. However, if we allow the injection of typed objects directly into a piece program "text" (structure), we can avoid the aforementioned capturing problem. To do so only requires the compiler to recognize semantic objects, like closures or macros, as constants (i.e., we inject semantic domains into the syntatic domains). We can then achieve the macro writers intension by writing frob as: (define frob (macro (x y z) `(,and ,x (,or ,y ,z)))) In this case the global binding of 'and' (presumably a macro object) at the time the macro is invoked, and NOT the symbol 'and', will be included in the transformed code, thus avoiding this category of variable capture For those who object to the predominance of commas in the definition of 'frob', let me postulate four new reader macros: ^ Constructs the corresponding list, but evaluates its atomic subcomponents. , Used to evaluate non-atomic subcomponents (i.e. an application). ` When used inside the scope of a "^", inhibits evaluation of the following (atomic) subcomponent. @ Splices the following subcomponent into a list. Frob could thus be written as: (define frob (macro (x y z) ^(and x (or y z)))) Lastly, we point out that the technique of including semantic domains in syntatic domains has merit in its own right. It is crucial in order to define macros which require data structures injected into the code, as in the definition of own variables or evaluate-once. It is also useful in inserting macro "help" functions directly into macro-expanded code, so as to share code between different macro-expansions of the same macro. We believe that this technique of "higher-dimensional programming" has much to offer, but has, to date, barely been explored. Binding injection: A similar problem arises when a lexical binding is injected into an existing lexical context because of a macro expansion. Consider the following two argument version of the macro or: (define or (macro (exp1 exp2) `(let [[v ,exp1]] (if v v ,exp2)))) This works fine, unless the second expression passed to or is 'v'. In this case: (lambda (v) (or exp1 v)) expands to: (lambda (v) (let [[v exp1]] (if v v v))) We then say the user variable v (i.e., exp2) is captured by the injected binding of v by the macro. We point out that such captures may actually be desired, although this technique is often frowned upon. The capture of variables due to injected bindings can be easily avoided. All that is required is to ensure that whenever a macro injects a binding, the macro must guarantee that the name of the injected bound variable is unique. Unique names can easily be created by a gensym-like function. Currently, there are two techniques that guarantee unique identifers in injected bindings. One technique requires explict programmer construction of unique names in the text of a macro. For example, the 'or' macro above could be defined as (define or (macro (exp1 exp2) (let [[new-var (gen-variable "or-var")]] ^(let [[new-var exp1]] (if new-var new-var exp2))))) Where (gen-variable ) constructs a unique, uninterned symbol, whose print name consists of concatenated with a (preferably) unique integer. Macro expansion of: (lambda (v) (or v)) would become: (lambda (v) ( [[or-var0001 ]] ( or-var0001 or-var0001 v))) which once again avoids capture. This category of variable capture due to injected bindings is addressed from a different perspective by Eugene Kohlbecker's note "Position Statement on Macros". Both approaches both attack the unpleasant aspects of "freezing" and "thawing" to get around name clashes by guarenteeing the uniquenes of injected binding identifiers. The Beta expansion which Eugene advocates differs from the approach above by 1) providing a framework which alleviates the user's concern for the variable clashes created by injected bindings, and 2) performing minimal "gensyming" of identifiers. -- brooks  Received: from brandeis by csnet-relay.csnet id ad02028; 18 Oct 84 20:09 EDT Received: by brandeis.ARPA (4.12/) id AA01394; Thu, 18 Oct 84 11:33:44 edt Date: Thu, 18 Oct 84 11:33:44 edt From: Myrna Fox To: scheme%mit-mc.arpa@csnet-relay.arpa Subject: Scheme Conference Participants Please stop at the information desk at Brandeis on Monday morning for directions to the conference area.  Received: from indiana by csnet-relay.csnet id ae03417; 18 Oct 84 1:23 EDT Date: Wed, 17 Oct 84 13:42:40 est From: Will Clinger Message-Id: <8410171842.AA05383@iuvax.UUCP> To: scheme%mit-mc.arpa@csnet-relay.arpa Subject: TI position (long message) Received: from ti-csl by csnet-relay.csnet id ab06137; 17 Oct 84 10:29 EDT Date: 16 Oct 1984 1740-CDT From: David Bartley Subject: TI "Position paper" To: willc@INDIANA cc: Bartley@TI-CSL Received: from csl60 by ti-csl; Wed, 17 Oct 84 07:41 CST Via: RAND-relay; 17 Oct 84 9:53-EST Will, here is a copy of the current TI-CSL (Computer Science Lab) position going into the SCHEME meeting next week. I had hoped to get it to you before you mailed out your agenda, but I don't think there is any harm done. Feel free to distribute it to the other participants. Thanks! -- David Bartley [Bartley@TI-CSL] --------------------------------------------------------------- TI-CSL Position on "Standardizing" SCHEME David Bartley Our Goals Texas Instruments is actively using SCHEME as an educational tool, systems programming language for AI and database architectures, and in research into the principles of programming languages and computer architectures. We have found the MIT videotape course and the recent book by Abelson and Sussman to be an excellent way to "rehabilitate" software developers within TI. Our experience so far shows that first-class functions and continuations are elegant and efficient mechanisms for expressing control for operating systems and simulation studies. We also are studying a "core" SCHEME which is suitable as an intermediate representation for other languages, including Common LISP, Pascal, and Prolog. Our philosophy on "standardizing" SCHEME at this time is as follows. MIT SCHEME has become a de facto standard within TI for educational purposes. Nearly a hundred programmers have taken the MIT course within TI during the last year or so, and there is already a demand for MIT SCHEME language support on our various computer facilities. On the other hand, our systems programming and research work has been with Indiana University's SCHEME 84. We are convinced that SCHEME (or at least a recognised "core") must remain a simple and elegant yet powerful and expressive language. Thus, we hope to reach agreement on at least those aspects of the language which are addressed in the MIT videotapes and book, while agreeing to cooperate on incorporating other aspects as they become mature. As a developer and manufacturer of products based on LISP, TI is determined to give full support to Common LISP. It is important to the success of SCHEME at TI that trivial incompatibilities between SCHEME and Common LISP be eliminated. This policy will also make it easier to develop a Common LISP system on top of SCHEME. We are committed to making SCHEME a first-class language throughout TI by developing first-class production quality implementations. To achieve this, we are prepared to invest in extensive compiler development work. Our perspective on "efficiency" is this: we intend to discover what architectural features are required to support languages appropriate for solving problems, not to tailor languages to the capabilities of current machines. On the other hand, it is imperative that we implement SCHEME efficiently on machines as small as 8088-based micros. As a research tool, our dialect of SCHEME will certainly diverge from other SCHEME implementations. (For example, we are investigating unification and multiple dynamic inheritance.) However, we are very eager to help establish a portable basis for the language. Such a basis should be constructed on a solid core of key concepts which together distinguish SCHEME from other languages, plus mechanisms for extending the base language. Portable programs can then carry their syntax with them, if necessary. We are encouraging this process for several reasons. First, aligning the dialects of SCHEME is a giant step towards building a larger community of SCHEME users and fans. A large user base is needed before we can expect a steady flow of quality software written in SCHEME. Second, a large, unified community can give SCHEME the credibility needed to co-exist with Common LISP and the other "name" languages. Third, TI is eager to build and strengthen ties with universities and other research organizations and individuals. The following sections of this paper briefly outline our opinions about various aspects of the design of the SCHEME language. Many of these topics are not yet mature enough to be standardized. Abbreviations: CL=Common LISP, IUS=Indiana SCHEME 84, MITS=MIT SCHEME (7th ed.), T=T (4th ed.), ZL=ZetaLISP. LEXICAL CONVENTIONS This is an area where compatibility with CL is critical. For example, alphabetic case should not be significant. However, the characters '!' and '?' should be first-class constituents. NAMING CONVENTIONS Generally speaking, we support the current convention in which a suffix '!' indicates side-effecting, suffix '?' indicates (pure?) predicates, infix '->' indicates coercions, etc. However, we hope to avoid trivial incompatibilities between SCHEME and CL. Allowing more than one name for the same thing may be acceptable, particularly as we migrate users away from our current dialects. SEMANTICS OF CONSTANTS The semantics of the names NIL and T must be clarified. If the name NIL is given special meaning by READ and PRINT (that is, both (FOO . NIL) and (FOO) read the same way) then NIL should not be merely a standard variable. We see no way to avoid the traditional interpretation that '() is both the empty list and the FALSE value. SEMANTICS OF VARIABLES We are unhappy with the concept of a single global environment as it currently exists in IUS and most LISPs. Several mechanisms for explicitly managed environments have been proposed. We would like to see a proposal that includes persistent objects and ZL/CL-style packages. We have several problems with the nature of DEFINE in MITS. Unfortunately, this is one of the most visible aspects of the language in the MIT course and book. We prefer the use of LET or LETREC/LABELS (see below) for extending the lexical environment. Binding/creation should be clearly distinguished from assignment and the scope should be clearly visible; DEFINE looks too much like assignment and has confusing semantics. Likewise, we tend to the view that SET! should not create new bindings. We prefer the name LETREC to LABELS; regardless, the values bound definitely should NOT be restricted to be LAMBDA expressions. This might be thought worthwhile if it guaranteed that the bindings were all compile-time only, but SET! prevents that. Also, it would rule out such innocent cases as the following. (letrec ((foo (let ((private-var 4)) (lambda (arg) body)))) body) We have several concerns about namespace issues for fluids, NIL and T, and syntactic elements of the language (keywords, macros): True DYNAMIC binding should be implemented using a name space of fluid identifiers which is DISJOINT from lexicals. Special forms to fetch, set, and bind fluids can distinguish the names via syntax. We prefer FLUID, FLUID-SET!, and FLUID-LET operations. FLUID-LAMBDA can be defined in terms of LAMBDA plus FLUID-LET. As mentioned above, NIL seems to be a distinguished name for the constant '(), since the reader and printer presumably treat them interchangeably. Treating the same name NIL as both a data item and a "standard variable" is confusing (but marginally acceptable). We prefer to think of such constants as immutable and therefore closer in spirit to parameterless macros than to variables. The case for the name T is much weaker; T is not treated specially by the reader or printer; its use as a non-NIL value is more a convention than a required language feature. We'd like to see special-casing of T removed from the language (cf. comments on ELSE, below). Regardless of the decision on T and NIL, the meaning of the following should be specified: (eq? T 'T) (eq? NIL 'NIL) (symbol? T) (symbol? NIL) (set! T x) (set! NIL x) (lambda (T)...) (lambda (NIL) ...) (T x y z) (NIL x y z) We have conducted a debate with IU for some time on how to distinguish syntactic elements (macros) from ordinary names (variables) while using the same lexical rules to construct instances of each. I think we are agreed that the two sets of names must be disjoint, but we have not agreed on what to do when new bindings involve names previously bound in the other namespace. This dilemma strikes at the heart of lexical scoping in SCHEME. Its solution will also require an answer to the problems of global variables and multiple environments. For esthetic and pragmatic reasons, we would like to find a solution that involves only local contextual information, in the spirit of lexical scoping. This would allow pretty-printers and screen editors to display more of the meaning of a program fragment to a user. SEMANTICS OF PROCEDURES Wherever possible, operations should return meaningful (or at least consistent) values. We suggest the following as examples: (set! a b) ==> b (exchange a b) ==> b ; from similarity to set! (print a) ==> a (newline) ==> nil As users, we appreciate the notational convenience of n-ary procedures -- MULAMBDA in IUS and the "dotted rest" parameter in T and MITS. We also think destructuring binds (e.g. T's DESTRUCTURE) can be useful. But we are concerned that we may lose the ability to reason mechanically about programs (e.g. type inference) the further SCHEME gets from its roots in the lambda calculus. We also feel that MITS has been overzealous in allowing such primitives as '>' to take an arbitrary number of arguments. When practically every common function takes an arbitrary number of arguments, it is hard to detect certain common user errors. SEMANTICS OF SPECIAL FORMS Syntactic extensions to SCHEME should obey lexical scoping rules, but must be distinct from lexical variable bindings. SET! should be generalized to include the semantics of SETF (CL). (Similarly for DEFINE, if it is an assignment operation instead of a binding operation.) We should avoid defining special forms that gratuitously side-affect the user's namespace by reserving words in certain ways. For example, ELSE is treated specially by COND in MITS and IUS and in CASE by IUS and T. Why not teach 'T or 'ELSE for COND or use IF instead? For CASE, we prefer the following syntax: (CASE exp (case1 . body1) (case2 . body2) ... ELSE . body) (uppercase is used only to distinguish terminal from non-terminal symbols) DATA TYPES At TI, SCHEME will eventually have to support all of the data types of CL and other languages. A distinguished UNBOUND type and ways to set and test for it are needed. It is not clear which references to an unbound value should cause an exception. Similarly, we intend to support IEEE floating point and its 'not-a-number' values. CALL/CC Upward continuations are a MUST for our purposes. We need to find ways to recognize downward-only continuations and optimize them. We need to decide what UNWIND-PROTECT means in the general case. We are interested in dealing with atomic transactions in SCHEME. UNDOing a transaction is similar to invoking a continuation but has other ramifications. LIBRARIES It may help us achieve portability of SCHEME programs if we can relegate groups of functions to optional libraries. With any luck, we'll be able to write the libraries themselves (or less efficient but more portable versions of them) in a standard subset of the language. I/O and extended MATH functions should probably be handled this way. MISCELLANEOUS COND, CASE, and IF should NOT "fall through" when no cases apply. An atom comparison primitive is needed, such as EQV? (MITS) or EQUIV? (T). It should probably be identical to CL's EQL. CASE should use EQV? or EQUAL? instead of EQ? Optimizing compilers can reduce the strength of the test for individual cases. We would like to explore ways to embed comments in the internal representation of source programs. We have considered ZL-style documentation strings and treating ';' as an infix operator. -- David Bartley, 16 October 1984, 1500ct [Bartley@TI-CSL] -------  Received: from brandeis by csnet-relay.csnet id am00862; 16 Oct 84 15:55 EDT Received: by brandeis.ARPA (4.12/) id AA21498; Tue, 16 Oct 84 09:49:00 edt Date: 16 Oct 1984 09:46-EST From: mw@brandeis In-Real-Life: Mitchell Wand,faculty Subject: Revised invitation list To: scheme@mit-mc Message-Id: <466782383/mw@brandeis> There were a couple of errors on the invitation list for the Scheme Meeting. The following people were omitted: Kent Dybvig (U of North Carolina) Bill Rozas (MIT) My apologies to these two.  Received: from indiana by csnet-relay.csnet id ai08264; 16 Oct 84 0:39 EDT Date: Mon, 15 Oct 84 20:30:54 est From: Will Clinger Message-Id: <8410160130.AA11798@iuvax.UUCP> To: scheme%mit-mc.arpa@csnet-relay.arpa Subject: feature list for workshop (long message) List of features to be considered at the workshop. For each of the following features (e.g. special forms, data types, and procedures) should the feature be essential -- every implementation of Scheme must have it. Programs written in Essential Scheme will run on any implementation of Scheme worthy of the name. optional -- not every implementation of Scheme will have it, but all implementations that have the feature will use the same syntax and semantics for the feature. Code that makes use of optional features will run on any implementation of Scheme that supplies the optional features. extended -- not every implementation of Scheme will have it, and the feature's syntax and semantics may vary even among implementations that have the feature. Code that makes use of extended features is not portable. (E.g. at the moment input and output is an extended feature.) ---------------------------------------------------------------- Lexical features The space character is a whitespace character. The tab character is a whitespace character. The newline character is a whitespace character. The carriage return character is a whitespace character. The line feed character is a whitespace character. The form feed character is a whitespace character. Any whitespace character is a delimiter. Left parenthesis is a delimiter. Right parenthesis is a delimiter. Left bracket is a delimiter. Right bracket is a delimiter. Left curly brace is a delimiter. Left parenthesis is a delimiter. Right curly brace is a delimiter. Semicolon is a delimiter. Single quote is a delimiter. Double quote is a delimiter. Backquote is a delimiter. End of file is a delimiter. Backslash is an escape character. Lists are opened by left parentheses and closed by right parentheses. When notating lists, matching left and right brackets are stylistic alternatives to matching left and right parentheses. Conses may be notated as (x . y), where y is any object. Possibly improper lists may be notated as (... x . y). Strings are delimited by double quotes. Double quotes may be included inside strings by escaping them. Quote marks have the meanings we have grown to know and love. Backquote and comma work as we are accustomed. With a backquoted list, comma at-sign works as we are accustomed. Comments are opened by semicolons and closed by end of line characters. Sharp sign macros are introduced by a sharp sign. Unreadable objects are printed using sharp sign notation. (We need to agree whether the notation is #{...} or #<...>.) Vectors may be written using sharp sign notation. (We need to agree whether the notation is #(...) or something else.) Binary numbers may be written using the #b notation. Octal numbers may be written using the #o notation. Decimal numbers may be written using the #d notation. Hexadecimal numbers may be written using the #x notation. Special characters may be written using the #\ notation. (We need to agree on the names for the special characters using this notation.) Miscellaneous objects may be written using the #[...] syntax. Miscellaneous objects may be written using the #! syntax. Integers may be written in the usual way, with or without signs. Floating point numbers may be written using decimal points. (We need to agree on the notation.) Floating point numbers may be written using exponents. (We need to agree on the notation.) Symbols may be written as sequences of characters that contain no special characters and begin with a character that cannot begin a number. Sequences of characters containing no special characters are interpreted as numbers if possible; if not possible they are interpreted as symbols. When the 26 letters of the alphabet are used to write a symbol, case is immaterial unless the letter is slashified. ---------------------------------------------------------------- Special variables (constants?) nil evaluates to the empty list. nil evaluates to some false value. t evaluates to some true value. else evaluates to some true value. ---------------------------------------------------------------- Special forms quote lambda (with a proper list of bound variables) lambda (with a symbol in place of a list of bound variables) lambda (with a possibly improper list of bound variables) lambda (with destructuring -- we need to agree on pattern syntax) mulambda let let* labels or letrec rec named-lambda if cond case and or conjunction or or disjunction locale make-environment the-environment access defrec (such that (defrec i e) means (define i (rec i e))) define (with what semantics?) set or set! do or step iterate block or sequence or begin block0 or begin0 catch (with dynamic extent restriction) catch (without dynamic extent restriction) throw delay (memoized) force (memoized) freeze (not memoized) thaw (not memoized) locative fluid-lambda fluid fluid-set! fluid-let or bind fluid-bound? dynamic-wind unwind-protect define-constant define-integrable define-syntax or ... ---------------------------------------------------------------- Data types The essential data types are disjoint. There is an object nil that represents false and the empty list. Procedures are a kind of object. Numbers are a kind of object. Symbols are a kind of object. Pairs (conses?) are a kind of object. Structures are a kind of object. Characters are a kind of object. Strings are a kind of object. Vectors are a kind of object. Bytevectors are a kind of object. Streams (channels? ports?) are a kind of object. Locales (environments?) are a kind of object. Syntax tables are a kind of object. Macros are a kind of object. Locatives (refs?) are a kind of object. ---------------------------------------------------------------- Procedures NULL? NULL? null? null? NIL? NOT NOT not not PROCEDURE? APPLICABLE? procedure? proc? APPLY APPLY apply apply call-with-current-continuation call/cc NUMBER? NUMBER? number? number? INTEGER? INTEGER? fix? RATIO? FLOAT? float? ODD? ODD? EVEN? EVEN? 1+ 1+ 1+ ADD1 add1 add1 -1+ -1+ 1- SUBTRACT1 sub1 sub1 + (...) + (...) + (2) + (2) plus (...) - (2) - (...) - (2) - (2) - (1) -- (1) minus (1) * (...) * (...) * (2) * (2) times (...) / / (...) / DIV QUOTIENT / / REMAINDER REMAINDER remainder remainder MOD INTEGER-DIVIDE ABS ABS abs abs GCD GCD MIN (...) MIN (...) min (...) min (...) MAX (...) MAX (...) max (...) max (...) random (1) random (1) = = =? = EQUAL? < < > >? > GREATER? N= NOT-EQUAL? >= >= >=? >= NOT-LESS? <= <= <=? <= NOT-GREATER? =0? 0? =0 ZERO? ZERO? <0? <0 NEGATIVE? NEGATIVE? >0? >0 POSITIVE? POSITIVE? N=0? NOT-ZERO? >=0? NOT-NEGATIVE? <=0? NOT-POSITIVE? EXP EXP exp LOG LOG log EXPT EXPT expt SQRT SQRT sqrt SIN SIN sin COS COS cos TAN TAN tan ASIN arcsin ACOS arccos arctan ATAN2 ATAN LOGAND logand LOGIOR logior LOGXOR logxor LOGNOT lognot lsh rot ASH BIT-FIELD SET-BIT-FIELD ->INTEGER fix ->FLOAT float FLOOR CEILING TRUNCATE ROUND All trigonometric functions take their arguments in radians. SYMBOL? SYMBOL? symbol? symbol? EQ? EQ? eq? eq? NEQ? SYMBOL->STRING pname ascii symbol->ascii STRING->SYMBOL CONCATENATE-SYMBOL concat ALPHALESS? alphaless? alpha< EXPLODE explode IMPLODE implode GENERATE-SYMBOL GENERATE-UNINTERNED-SYMBOL gensym gensym PAIR? PAIR? cons? pair? (?) ATOM? ATOM? atom? atom? (?) LIST? PROPER-LIST? LIST? ALIKE? ALIKEQ? ALIKEV? EQUAL? equal? equal? CONS CONS cons cons LIST LIST list list CONS* LIST* COPY-LIST copy CAR CAR car car CDR CDR cdr cdr C....R C....R c..r c...r NTH LIST-REF nth NTHCDR LIST-TAIL NTH NTHCDR LAST LASTCDR LAST last-pair FIRST SECOND THIRD FOURTH FIFTH SIXTH SEVENTH EIGHTH SET-CAR! set-car! set-car! SET-CDR! set-cdr! set-cdr! LENGTH LENGTH length length APPEND (...) APPEND (>=2) append (2) append (>=2) APPEND! (...) append! (>=2) REVERSE REVERSE reverse reverse REVERSE! reverse! SUBLIST SUBST SUBSTQ SUBSTV subst COPY-TREE MEM? MEMQ? MEMQ memq memq MEMV MEMBER member member ASS ASSQ ASSQ assq assq ASSV ASSOC assoc assoc ANY? EVERY? DEL DELQ DEL! DELQ! delq! delete! MAP MAPCAR mapcar mapcar MAPCDR MAP! WALK MAPC mapc mapc WALKCDR TREE-HASH The car of nil is nil. The cdr of nil is nil. STRING? string? string? string-equal? ALPHALESS? alphaless? alpha< MAKE-STRING STRING-APPEND COPY-STRING CHAR->STRING LIST->STRING list-to-string STRING->LIST string-to-list STRING-LENGTH STRING-EMPTY? STRING-ELT NTHCHAR et cetera VECTOR? VECTOR? vector? vector-equal? MAKE-VECTOR VECTOR VECTOR-CONS vector-cons LIST->VECTOR list-to-vector VECTOR->LIST vector-to-list VECTOR-ELT VECTOR-REF vector-ref VREF VSET VECTOR-SET! vector-set! COPY-VECTOR VECTOR-FILL VECTOR-REPLACE VECTOR-LENGTH VECTOR-SIZE vector-length VECTOR-POS VECTOR-POSQ WALK-VECTOR ---------------------------------------------------------------- Input and output All input and output is routed through the stream to which certain distinguished variables are bound. There is a way to read an object from the standard input. There is a way to write objects to the standard output, with slashification. There is a way to write objects to the standard output, without slashification. There is a way to write a string to the standard output, without the double quotes. There is a way to write an end of line to the standard output. There is a way to read a character from the standard input. There is a way to peek at a character from the standard input. There is a way to write a character to the standard output. There is a way to open a file for input. There is a way to open a file for output. There is a way to close an open file. There is a way to test for end of file on an open input file. There is a way to test if an object that has been read is the end of file. There is a way to pretty print an object on the standard output. There is a way to pretty print some approximation to the definition of a procedure on the standard output. There is a way to generate a transcript file.  Received: from indiana by csnet-relay.csnet id ah08264; 16 Oct 84 0:36 EDT Date: Mon, 15 Oct 84 20:27:55 est From: Will Clinger Message-Id: <8410160127.AA11758@iuvax.UUCP> To: scheme%mit-mc.arpa@csnet-relay.arpa Subject: questions for workshop (long message) List of questions to be considered at the workshop. ---------------------------------------------------------------- Nil, t, and else... Should (nil) be the same as (()) ? Should () evaluate to the same thing as '() ? Should nil evaluate to the same thing as () ? Should nil evaluate to the same thing as '() ? Should 'nil evaluate to the same thing as nil ? Should nil be a symbol? If nil should be a symbol, should its evaluation semantics be those of a: static constant lexical constant lexical variable Should 't evaluate to the same thing as t ? Should t be a symbol? If t should be a symbol, should its evaluation semantics be those of a: static constant lexical constant lexical variable Should else be a: noise word recognized by certain special forms static constant lexical constant lexical variable ---------------------------------------------------------------- Lexical conventions... Should the following characters be alphabetic? [ ] { } If they are not alphabetic, what are they? Should symbols be converted to upper case unless slashified? Should slashification be indicated by backslash for single characters, as in \" ? And by enclosure in vertical bars for entire symbols, as in |"| ? Should abc|def|g be the same symbol as abcdefg? For the #\ character syntax, which of the following are the correct choices? #\SP #\SPACE (ascii " ") #\DOT #\PERIOD (ascii ".") #\LF #\NEWLINE (ascii " ") #\CR none of the above Unreadable objects should be printed as #{...} #<...> something else Is the #!... syntax a good idea for miscellaneous objects? ---------------------------------------------------------------- Scope rules for keywords of special forms... Should the keywords of special forms have global or lexical scope? Should the keywords of special forms be shadowed by lambda-binding? Should the keywords of special forms be shadowed by local syntax definitions? Should the keyword of a special form be assigned using set! ? What should happen when the keyword of a special form appears other than in the car position of a form? ---------------------------------------------------------------- Names of special forms... Should it be (lambda x ...) or (mulambda x ...) ? Should it be (lambda (a b . c) ...) or (mumulambda (a b . c) ...) or something else altogether? For each of the following sets of names of special forms, choose the better: { labels, letrec } { and, conjunction } ; also { disjunction, or } { define, define!, defrec, defrec! } ; meaning IU's defrec { define, define!, lset } ; for variables { set, set! } { do, step } { begin, block, sequence } { bind, fluid-let } ---------------------------------------------------------------- Semantics of constants... Which of the following should evaluate to themselves? nothing numbers strings characters everything except symbols and conses Side effects to quoted constants should be ok be an error signal an error If side effects to quoted constants are ok, then should structured constants be freshly consed? ---------------------------------------------------------------- Semantics of definitions... For the define special form, should (define (foo ...) ...) be equivalent to (define foo (lambda (...) ...)) ? Should (define ((foo ...) ...) ...) be equivalent to (define (foo ...) (lambda (...) ...)) ? Et cetera? Should (define (foo x) (define (bar y) ...) (define (baz z) ...) ...) be closer in its semantics to (define (foo x) (letrec ((bar (lambda (y) ...)) (baz (lambda (z) ...))) ...)) or to (define (foo x) (set! bar (lambda (y) ...)) (set! baz (lambda (z) ...)) ...) Assuming the first answer on the previous question, should embedded definitions be required to come first in a definition, or should (define (foo x) (print (bar 3)) (define (bar y) ...) (foo (+ x 1))) be legal? If embedded definitions are not required to come first in a definition, what should their semantics be? Assuming embedded definitions are required to come first in a definition, should embedded definitions be allowed in a lambda? In other words, should (mapcar (lambda (x) (define (bar y) ...) (define (baz z) ...) ...) ...) be legal? What value should be returned by a define form? ---------------------------------------------------------------- Semantics of fluid variables... Should (let ((x 3)) (fluid-let ((x 4)) x)) evaluate to 3 or to 4? ---------------------------------------------------------------- Ontology of variables... Should lexical variables be the only kind of variable, semantically speaking? If there are semantically important variables other than lexical variables, then which of the following kinds of variables should there be: fluid variables locale variables global variables base variables other kinds of variables ---------------------------------------------------------------- Semantics of special forms... Cond clauses should have: exactly two forms two or more forms one or more forms If no cond clause is selected, then: the value of the cond expression should be nil it should be an error an error should be signalled Each pattern of a case clause should consist of: a value (on which eq? is meaningfully defined) a value or list of values If no case clause is selected, then: the case expression should return nil it should be an error an error should be signalled In the following transcript, what should happen? >>>(define foo (lambda (n) (if (0? n) t (foo (1- n))))) foo >>>(define bar foo) bar >>>(bar 3) t >>>(define foo (lambda (x) (list x))) foo >>>(bar 3) ??? ; t or (2)? Should set (set!) have the setf feature? The do (step) special form should have: exactly one return form one or more return forms (all but the last are executed for effect) zero or more return forms (if zero, then the value of the test is returned) Should it be permissible to omit update expressions for do (step) variables? (begin) should return nil be an error signal an error ---------------------------------------------------------------- Macros... Does anybody have a syntax and semantics for macros that is good enough and stable enough that we should consider committing ourselves to it for a period measured in years? ---------------------------------------------------------------- Input and output... The following programs prompt for input, read input, print output to a file, and then repeat the cycle. These examples are intended to illustrate incompatibilities between the various implementations in the matter of input and output. ; T (fourth edition) (define main (labels ((get-list (lambda () (display "Please give me a list to reverse: " (standard-output)) (read (standard-input))))) (lambda (filename) (let ((output (open filename '(out)))) (do ((list (get-list) (get-list))) ((null? list) (close output)) (print (reverse list) output) (newline output)))))) ; MIT Scheme (seventh edition) (define (main filename) (define (get-list) (princ "Please give me a list to reverse: ") (read)) (define output (open-printer-channel filename)) (define (loop list) (if (null? list) (close-channel output) (sequence (print (reverse (list)) output)) (loop (get-list))))) (loop (get-list))) ; Scheme 312 Version -3.00 (define main (letrec ((get-list (lambda () (printstring "Please give me a list to reverse: ") (read)))) (lambda (filename) (let ((output (outfile filename))) (do ((list (get-list) (get-list))) ((null? list) (close output)) (print (reverse list) output) (newline output)))))) ; Scheme 84 Version 0.5 (define main (letrec ((get-list (lambda () (print "Please give me a list to reverse: ") (read)))) (lambda (filename) (let ((output (open filename 'write))) (do ((list (get-list) (get-list))) ((null? list) (close output)) (fluid-let ((output-port output)) (display (reverse list)) (newline))))))) Is the Scheme 84 idea of routing all I/O through fluid identifiers a good one? How should the following basic I/O operations be done? Read an object from the standard input: (read (standard-input)) (read) Print an object to the standard output, with slashification: (print object (standard-output)) ; T (prin1 object) ; MIT Scheme (display object) ; Scheme 84 Print an object to the standard output, without slashification: (display object (standard-output)) ; T (princ object) ; MIT Scheme (print object) ; Scheme 84 Print a string to the standard output, without the double quotes. (write-string (standard-output) object) ; T (princ object) ; MIT Scheme (printstring string) ; Scheme 312 (print object) ; Scheme 84 Write an end of line to the standard output: (newline (standard-output)) (newline) Read a character from the standard input: (read-char (standard-input)) ; T (tyi) ; MIT Scheme (tyi %conin) ; Scheme 312 (read-char) ; Scheme 84 Peek at a character from the standard input: (block0 (read-char (standard-input)) (unread-char (standard-input))) (peekch) (tyipeek) Write a character to the standard output: (write-char (standard-output) char) ; T (tyo char) ; MIT Scheme (tyo char %conout) ; Scheme 312 Open a file for input: (open filename '(in)) ; T (open-reader-channel filename) ; MIT Scheme (infile filename) ; Scheme 312 (open filename 'read) ; Scheme 84 Open a file for output: (open filename '(out)) ; T (open-printer-channel filename) ; MIT Scheme (outfile filename) ; Scheme 312 (open filename 'write) ; Scheme 84 Close an open file: (close object) (close-channel channel) Test for end of file on an open input file: (block0 (eof? (read-char stream)) (unread-char stream)) (let ((eof '(eof))) (eq? (peekch channel eof) eof)) (=? (tyipeek port) -1) Test if an object that has been read is the end of file: (eof? object) (eq? object EOF) Pretty print an object on the standard output: (pretty-print object (standard-output)) ; T (pp object) ; MIT Scheme (pretty-print object) ; Scheme 312 Pretty print (some approximation to) the definition of a procedure on the standard output: (pp name-of-procedure) ; T (pp procedure) ; MIT Scheme (pretty '(name-of-procedure)) ; Scheme 312, Scheme 84 Generate wallpaper: (transcript-on filename) ; T, Scheme 84 (photo filename) ; MIT Scheme Stop generating wallpaper: (transcript-off) ; T, Scheme 84 (tofu) ; MIT Scheme ---------------------------------------------------------------- Naming conventions... Are the T naming conventions for procedures acceptable? Should the names of variables whose values are not procedures or whose value is intended to change begin and end with asterisks, with t and nil as exceptions to the rule? ---------------------------------------------------------------- Procedures... Should various sorts of procedures be distinguishable at run time? (E.g. primitives, system functions, continuations, fluid-closures, closures, engines.) Which is the better name? { procedure?, proc?, applicable? } ---------------------------------------------------------------- Numbers... Should various sorts of numbers be distinguishable at run time? (E.g. fixnums, bignums, ratios, floats, complexes.) Which is the better name? { integer?, fix? } { -1+, 1- } { subtract1, sub1 } { div, quotient, / } { =, =? } { <, , >? } { >=, >=? } { <=, <=? } { =0?, 0?, =0 } { <0?, <0 } { >0?, >0 } { N=0?, <>0?, <>0, !=0?, !=0, not=0?, nonzero? } { atan, arctan } { atan2, atan } ; two arguments { ->integer, fix } { ->float, float, floor, ceiling, truncate, round } Should trigonometric functions take their arguments in radians? ---------------------------------------------------------------- Symbols... Which is the better name? { symbol->string, pname } { concatenate-symbol, concat } { alphaless?, alpha< } { generate-symbol, generate-uninterned-symbol, gensym } ---------------------------------------------------------------- Lists... Which is the better name? { pair?, cons? } { proper-list?, list? } ; true if cdr of last cons is nil { alikev?, equal? } { nth, list-ref } { nthcdr, list-tail } { lastcdr, last, last-pair } { map, mapcar } { walk, mapc } Taking the car or cdr of nil should: yield nil be an error signal an error atom? is true of what things? ---------------------------------------------------------------- Strings... Which is the better name? { list->string, list-to-string } { string->list, string-to-list } ---------------------------------------------------------------- Vectors... Which is the better name? { list->vector, list-to-vector } { vector->list, vector-to-list } { vector-elt, vector-ref } { vset, vector-set! } { vector-length, vector-size }  Received: from indiana by csnet-relay.csnet id ag08264; 16 Oct 84 0:36 EDT Date: Mon, 15 Oct 84 20:24:56 est From: Will Clinger Message-Id: <8410160124.AA11707@iuvax.UUCP> To: scheme%mit-mc.arpa@csnet-relay.arpa Subject: agenda for workshop Agenda for workshop on Scheme. First day. Distribution of agenda, list of questions, list of features, and supporting materials. [10 minutes] Moderator's statement of purpose and procedure. [20 minutes] Straw poll using list of questions. [1 hour] Nominations of new questions to be added to the list. [30 minutes] Discussion of questions. [4 hours] nil, t, and else [15 minutes] lexical conventions [10 minutes] scope rules for keywords of special forms [20 minutes] names of special forms [10 minutes] semantics of constants [5 minutes] semantics of definitions [20 minutes] semantics of fluid variables [5 minutes] ontology of variables [15 minutes] semantics of special forms [10 minutes] macros [30 minutes] input and output [30 minutes] naming conventions [5 minutes] procedures [5 minutes] numbers [15 minutes] symbols [5 minutes] lists [10 minutes] strings [5 minutes] vectors [5 minutes] new topics added by the participants [20 minutes] Identification of research areas that need more work and discussion of research under way. [2 hours] Second day. Final vote on list of questions. [3 hours] Discussion of list of features. [2 hours] lexical features [25 minutes] special variables [5 minutes] special forms [30 minutes] data types [15 minutes] procedures [30 minutes] input and output [15 minutes] Final vote on list of features. [2 hours] Arrangements for future coordination. [1 hour]  Received: from indiana by csnet-relay.csnet id a008264; 16 Oct 84 0:35 EDT Date: Mon, 15 Oct 84 20:23:51 est From: Will Clinger Message-Id: <8410160123.AA11681@iuvax.UUCP> To: scheme%mit-mc.arpa@csnet-relay.arpa Subject: 22-23 October workshop In subsequent messages I am sending 1) the tentative agenda for the 22-23 October workshop on Scheme. 2) the tentative list of questions referred to in the agenda. 3) the tentative list of features referred to in the agenda. Comments, criticisms, suggestions, and counter-proposals are welcome. Indeed, they are URGENT -- if they reach me later than Friday I may simply ignore them. Peace, Will Clinger willc%indiana@CSNET-RELAY  Received: from mit-mc.arpa by csnet-relay.arpa id a002475; 13 Oct 84 22:09 EDT Date: Sat, 13 Oct 1984 22:05 EDT Message-ID: From: CPH%MIT-OZ%mit-mc.arpa@csnet-relay.arpa To: mw%brandeis.csnet%csnet-relay.arpa@csnet-relay.arpa Cc: dfried%indiana.csnet%csnet-relay.arpa@csnet-relay.arpa, lang-scheme%indiana.csnet%csnet-relay.arpa@csnet-relay.arpa, oxley%ti-csl.csnet%csnet-relay.arpa@csnet-relay.arpa, scheme%mit-mc.arpa%csnet-relay.arpa@csnet-relay.arpa Subject: Scheme meeting Please note that Chris Terman has nothing whatsoever to do with this meeting; I am the person that should be listed. I also (firmly) request that Bill Rozas (Jinx@MIT-OZ) be added to the invitation list. He has done a great deal of excellent work here and is at least as entitled to be present as anyone else on the list, as I am sure GJS will be quick to agree.  Received: from brandeis by csnet-relay.csnet id ak01935; 13 Oct 84 19:25 EDT Received: by brandeis.ARPA (4.12/) id AA19524; Fri, 12 Oct 84 15:06:58 edt Date: Fri, 12 Oct 84 15:06:58 edt From: Myrna Fox To: scheme%mit-mc.arpa@csnet-relay.arpa Subject: Workshop Attendees If you call the Marriott directly, please tell them you're with us.  Received: from brandeis by csnet-relay.csnet id ab00293; 10 Oct 84 19:28 EDT Received: by brandeis.ARPA (4.12/) id AA03336; Wed, 10 Oct 84 14:47:11 edt Date: 10 Oct 1984 14:17-EST From: mw%brandeis.csnet@csnet-relay.arpa In-Real-Life: Mitchell Wand,faculty Subject: Scheme meeting To: scheme%mit-mc.arpa@csnet-relay.arpa Cc: lang-scheme%indiana.csnet@csnet-relay.arpa, oxley%ti-csl.csnet@csnet-relay.arpa, dfried%indiana.csnet@csnet-relay.arpa Message-Id: <466280247/mw@brandeis> WORKSHOP ON THE PROGRAMMING LANGUAGE SCHEME October 22-23, 1984 Brandeis University Waltham, MA ********************************************************************** General Chairman: Dan Friedman, Indiana Agenda Committee: Will Clinger, Indiana (chair) Chris Terman, MIT Gary Brooks, Indiana Jonathan Rees, Yale/Dec/MIT David Bartley, TI Local Arrangements: Mitch Wand, Indiana/Brandeis/MIT The purpose of this workshop is to identify those features of Scheme which may be made common among various implementations, to discuss the nature and format of interim standards for Scheme, and to expose some of the problems which may make other features difficult to standardize. *********************************************************************** Starting Time: 9:00 am, Mon, Oct 21, 1984 The first day's sessions will be held in the Usdan Student Union. The second day's sessions will be held in Ford Hall, home of the Brandeis Computer Science Department. *********************************************************************** Registration: The cost of the conference will be $25. This covers lunches on both days, plus snacks, etc. Accommodations: We have reserved a block of rooms in the Marriott Inn, Commonwealth Avenue, Newton, MA. (969-1000). Cost is $94/day for single, $104/day for double (ouch! sorry 'bout that). Cheaper accomodations may be found at the Holiday Inn on Grove Street in Newton. Transportation: Brandeis is about 12 miles from Logan Airport in Boston. We recommend that participants have access to rental cars, as public transportation from the airport to Brandeis is difficult, and there are no motels within walking distance of the campus. Welcoming reception: None. (You thought maybe this was a POPL?) Banquet: We have made arrangements for a Chinese buffet at the Peking Restaurant in Lexington, for 700pm on Monday night. ********************************************************************** Attendance: Attendance is by invitation only, as this is a working meeting. The following is the current invitation list Abelson (MIT) Adams (Yale) Bartley (TI) Brooks (IU/TI) Clinger (IU) Friedman (IU) Gabriel, Dick (Stanford) Halstead, Bert (MIT) Hanson (MIT) Haynes (IU) Kohlbecker (IU) Oxley (TI) Pitman (MIT) Rees (MIT/Yale) Steele (Tartan Labs) Sussman (MIT) Wand (IU/Brandeis) If you are not on this list, please accept the apologies of the organizers. ****************************************************************** Registration: If you are on this list, please let us know whether or not you plan on attending. We need to have a definite list by Mon Oct 15. Mail electronically to: maf%brandeis.csnet@csnet-relay (CSnet/Arpanet) or by calling Myrna Fox at 617 647 2119. Again, we apologize for the short notice on this final announcement; it is my understanding that the dates and place were circulated earlier. ********************************************************************  Date: 29 September 1984 19:57-EDT From: Jonathan A Rees Subject: Another test message To: SCHEME @ MIT-MC This is another test message. Many new recipients have been added. Watch this space for an announcement of what this mailing list is about. Jonathan  Date: 24 September 1984 18:42-EDT From: Jonathan A Rees Subject: test message To: SCHEME @ MIT-MC Test message.