Date: Tue, 23 Apr 85 16:25:21 EST From: Jonathan A Rees Subject: Two armed IF is essential. To: SCHEME@MIT-MC, linus!ramsdell@MITRE-BEDFORD In-reply-to: Msg of 22 Apr 1985 18:36:48-EST Mon 22 Apr 85 09:57:17 est from linus!ramsdell at Mitre-Bedford, linus!ramsdell (John D. Ramsdell) at Mitre-Bedford Message-ID: <[MIT-MC].466615.850423.JAR> Date: 22 Apr 1985 18:36:48-EST From: linus!ramsdell at Mitre-Bedford One-armed IF may change from essential to optional status, but please don't change two-armed IF. I think this was what Will intended to write. The terminology is confusing, since an IF with N arms has N+1 subforms, and he probably wrote arm and meant subform. So: (IF test consequent alternate) essential (IF test consequent) optional Jonathan  Received: from csnet-relay by MIT-MC.ARPA; 23 APR 85 11:58:20 EST Received: from indiana by csnet-relay.csnet id ad08662; 23 Apr 85 11:52 EST Date: Tue, 23 Apr 85 11:12:36 est From: Perry Wagle Received: by iuvax.UUCP; id AA11641; Tue, 23 Apr 85 11:12:36 est To: scheme%indiana.csnet@csnet-relay.arpa Subject: Retraction and Resubmission I noticed later that my suggestion about the renaming of "mapc" is somewhat bogus, I apologize: Mapcar-type functions have two primary operating parameters: (1) the order in which the list is processed, (2) a resultant value from whether the list is copied, side-effected, or forgotten. The current "mapcar" processes in arbitrary order and returns the copied list. The current "mapc" processes in left-to-right order and forgets the list (returning nothing). Quite, ahem, different. (The most direct interpretation of a function named "mapcar!" would be one that set-car!ed the list, returned the side-effected list, and was open as to whether to process in arbitrary or left-to-right order.) I would have use for (in order of frequency): (1) arb-order, copy list, return list. (the current mapcar). (2) LR-order, copy list, return list. I don't like set-car!ing cons-cells, but I make much use of files and continuations, so order matters a lot. (3) LR-order, forget list, return nothing. (the current mapc). Print lists. (4) LR-order, side-effect list, return list. If keeping same cons-cell is important for some reason. Perhaps only a subset of the set of useful mapcar variations should be provided by the system - the two that already are? The points would still remain of denoting their status as a member of this grouping by using the word "map" in the name, of having special marking for functions that guarantee left-to-right evaluation or processing, and having a naming convention for the others should they ever be realized. My suggestion is to distinguish between the arbitrary and left-to-right list processing but starting off with two roots "map" and "map/lr", respectively. Futhermore, since there are three values returned - a copied list, a set-car!ed list, and nothing at all - "copy", "list!", and "" (null), respectively. Then the four hypothetical functions above would be named: (1) "map->copy" (2) "map/lr->copy" (3) "map/lr" (4) "map/lr->list!" or "map/lr->same!" or ... (?) (Note that while there would be no reasonable value for (a fifth) "map" to return, while "map/lr" could return the value of the last mapping.) Perry Wagle  Received: from mitre-bedford by MIT-MC.ARPA; 22 APR 85 18:44:29 EST Date: 22 Apr 1985 18:36:55-EST From: linus!ramsdell@Mitre-Bedford Received: by linus.UUCP (4.12/4.7) id AA12343; Mon, 22 Apr 85 10:34:00 est Date: Mon, 22 Apr 85 10:34:00 est From: linus!ramsdell (John D. Ramsdell) Message-Id: <8504221534.AA12343@linus.UUCP> To: bccvax!scheme@mit-mc.arpa Subject: REC => LABEL I know its too late, but I support Mitch Wand's suggested rename for REC. John  Received: from mitre-bedford by MIT-MC.ARPA; 22 APR 85 18:43:53 EST Date: 22 Apr 1985 18:36:48-EST From: linus!ramsdell@Mitre-Bedford Received: by linus.UUCP (4.12/4.7) id AA11664; Mon, 22 Apr 85 09:57:17 est Date: Mon, 22 Apr 85 09:57:17 est From: linus!ramsdell (John D. Ramsdell) Message-Id: <8504221457.AA11664@linus.UUCP> To: bccvax!scheme@mit-mc.arpa Subject: Two armed IF is essential. >>>From bccvax!willc%indiana.csnet@csnet-relay.arpa Sun Apr 21 14:30:50 1985 >>>... >>>Two-armed if will change from essential to optional status. One-armed IF may change from essential to optional status, but please don't change two-armed IF. John  Received: from MIT-VAX by MIT-MC via Chaosnet; 22 APR 85 16:02:23 EST Received: by mit-vax.Mit-chaos.Arpa (4.12/4.8) id AA08654; Mon, 22 Apr 85 16:02:07 est Date: Mon, 22 Apr 85 16:02:07 est From: Bert Halstead To: JAR@MIT-MC Subject: Re: hashing Cc: SCHEME@MIT-MC Roger, I'm sensitive to the practical issues you raise, as to the mechanics of getting agreement on the Scheme report. I don't object to object-hash and object-unhash as optional Scheme functions -- I was just flaming about implementation considerations which, if they had occurred to me during the workshop, I had long since forgotten about. It does seem like the issues of long-lived systems, and the cost of maintaining a hash space that is ultimately sparsely populated, provide some food for thought. But that's one thing that optional features are for. -b.  Received: from csnet-relay by MIT-MC.ARPA; 22 APR 85 15:24:22 EST Received: from indiana by csnet-relay.csnet id a002853; 22 Apr 85 15:16 EST Date: Mon, 22 Apr 85 13:54:19 est From: Perry Wagle Received: by iuvax.UUCP; id AA17597; Mon, 22 Apr 85 13:54:19 est To: scheme%indiana.csnet@csnet-relay.arpa Subject: mapcar and mapc In response to Will Clinger's acceptance of the "mapc" -> "for-each" renaming: How about renaming "mapc" to "mapcar!" (and thence to "map!")? This would change the meaning of the bang notation from "Hi! I'm a side-effecting function" to "Hi! I'm expecting either myself or one of the functions that I call to be a side-effecting function". This way, the two very simular functions "map" and "map!" keep simular names, making life easier on the frail memory of the user. Perry Wagle  Date: Mon, 22 Apr 85 14:12:52 EST From: Jonathan A Rees Subject: hashing To: JINX@MIT-OZ cc: SCHEME@MIT-MC, rhh@MIT-VAX In-reply-to: Msg of 21 Apr 1985 22:24 EST (Sun) from Bill Rozas Message-ID: <[MIT-MC].464817.850422.JAR> Date: 21 Apr 1985 22:24 EST (Sun) From: Bill Rozas Instead of hash-object and unhash-object, why don't we include populations? The implementation can be done in terms of hash and unhash object or more primitively, and they seem to constitute the most common use of these procedures. I don't think populations are much easier to implement than hash/unhash, and they aren't as general. T started out having populations, and a year or two later acquired OBJECT-HASH, which was applicable in many situations where populations wouldn't do. (I think you might be able to implement hash/unhash in terms of populations, but it's much less natural than vice versa.) Uses for hash/unhash include populations, weak tables, and file closing, as well as the following interesting hack, which has tremendous popularity with users: PRINT calls OBJECT-HASH when it comes across an object with no standard external representation, a procedure for example, and prints the hash number along with any other type and identification info it can find: e.g. > cadr ;User's typein #{Procedure 31 CADR} ;System's typeout > (lambda (x) (* x 3)) #{Procedure 32} > Although the syntax #{...} isn't readable (that could cause very bizarre results for READ's done in processes different from the one which printed the object), you can evaluate (OBJECT-UNHASH 32) and, if the garbage collector hasn't done away with it, get the procedure back. This is vaguely like "D-lines" in Macsyma, except that it permits reclamation. Note that hash is called only when PRINT is called, so there is no evaluator overhead involved. (One user was at first baffled by the following interaction: > (lambda () 1) #{Procedure 33} > (lambda () 2) #{Procedure 34} > (do ((i 0 (1+ i)) (p nil (lambda () i))) ((= i 100) p)) #{Procedure 35} > and then figured out that PRINT was generating the numbers.)  Date: Mon, 22 Apr 85 13:40:27 EST From: Jonathan A Rees Subject: hashing To: rhh@MIT-VAX cc: SCHEME@MIT-MC In-reply-to: Msg of Sun 21 Apr 85 17:43:51 est from Bert Halstead Message-ID: <[MIT-MC].464776.850422.JAR> Date: Sun, 21 Apr 85 17:43:51 est From: Bert Halstead ... This implies that 77 can NEVER be reused as the hash code for some other object passed to hash-object! This is an intentional feature of the proposal; if recycling is permitted then the facility takes on a totally different character and has more liimted application. Feature: Non-re-use means that OBJECT-UNHASH, which is desirable for many applications of these things (like post-GC automatic file closing, weak tables, and populations), is possible and meaningful. Antifeatures: (1) Non-re-use means that OBJECT-HASH is different from Maclisp MAKNUM, and is therefore, presumably, more expensive. (2) Non-re-use means that in very-long-lived systems, hash numbers might go into bignums. --- The hash/unhash facility is already optional, and I would suggest that if for some reason you don't like its details you can just not implement it. If there is much disagreement now about the facility, then it should be removed from the report. This removal should be done carefully, since we agreed on it last fall, and agreements are precious; a different hash facility probably won't make it into the report. Jonathan  Received: from MIT-OZ by MIT-MC via Chaosnet; 21 APR 85 22:31:16 EST Date: Sun, 21 Apr 1985 22:29 EST Message-ID: From: CPH%MIT-OZ@MIT-MC.ARPA To: Bill Rozas Cc: Bert Halstead , scheme@MIT-MC Subject: reactions to Will's updates In-reply-to: Msg of 21 Apr 1985 22:24-EST from Bill Rozas Date: Sunday, 21 April 1985 22:24-EST From: Bill Rozas Instead of hash-object and unhash-object, why don't we include populations? The implementation can be done in terms of hash and unhash object or more primitively, and they seem to constitute the most common use of these procedures. Actually, the major reason that we implemented hashing is to have generalized two dimensional property tables, for generic operations.  Received: from MIT-OZ by MIT-MC via Chaosnet; 21 APR 85 22:29:25 EST Date: Sun, 21 Apr 1985 22:28 EST Message-ID: From: CPH%MIT-OZ@MIT-MC.ARPA To: Bill Rozas Cc: Scheme@MIT-MC.ARPA Subject: lexical syntax for numbers In-reply-to: Msg of 21 Apr 1985 22:20-EST from Bill Rozas Date: Sunday, 21 April 1985 22:20-EST From: Bill Rozas Why don't we fix the order of the exactness, radix, and precision modifiers? I don't think anything is gained from allowing arbitrary order and the parser may become simpler if it can easily default. One thing that is gained from arbitrary order is that one need not remember what the particular order is -- a great advantage to people who don't remember trivial details very well and don't like reading manuals.  Received: from MIT-OZ by MIT-MC via Chaosnet; 21 APR 85 22:25:23 EST Date: 21 Apr 1985 22:24 EST (Sun) Message-ID: From: Bill Rozas To: Bert Halstead Cc: scheme@MIT-MC Subject: reactions to Will's updates In-reply-to: Msg of 21 Apr 1985 17:43-EST from Bert Halstead Instead of hash-object and unhash-object, why don't we include populations? The implementation can be done in terms of hash and unhash object or more primitively, and they seem to constitute the most common use of these procedures.  Received: from MIT-OZ by MIT-MC via Chaosnet; 21 APR 85 22:22:06 EST Date: 21 Apr 1985 22:20 EST (Sun) Message-ID: From: Bill Rozas To: Will Clinger Cc: scheme@MIT-MC.ARPA Subject: lexical syntax for numbers In-reply-to: Msg of 20 Apr 1985 15:33-EST from Will Clinger Two comments: Why can't exponents have + signs, and why must they be decimal? It seems to me that they should be any valid representation of an integer, though they may always want to be exact. Why don't we fix the order of the exactness, radix, and precision modifiers? I don't think anything is gained from allowing arbitrary order and the parser may become simpler if it can easily default.  Received: from MIT-VAX by MIT-MC via Chaosnet; 21 APR 85 17:44:14 EST Received: by mit-vax.Mit-chaos.Arpa (4.12/4.8) id AA03260; Sun, 21 Apr 85 17:43:51 est Date: Sun, 21 Apr 85 17:43:51 est From: Bert Halstead To: scheme@mc Subject: reactions to Will's updates unhash-object sounds like a bad idea. In Will's scenario, if an object (0 . 0) is passed to hash-object, and, say, 77 is returned, subsequent calls to (unhash-object 77) must return either (0 . 0) or #!false (presumably the latter is an out if the (0 . 0) has been garbage- collected). This implies that 77 can NEVER be reused as the hash code for some other object passed to hash-object! It also implies (do we want this?) that hash-object is a one-to-one function -- it is never permissible for more than one object to map to the same hash code. This is not necessary if hash-object is just a way to compute an index into some user-maintained hash table, where objects could be associated with whatever information is desired. re numbers: Is anything substantial gained by disallowing explicit "+" signs on the exponents of numbers in scientific notation? -Bert  Received: from csnet-relay by MIT-MC.ARPA; 21 APR 85 09:52:17 EST Received: from indiana by csnet-relay.csnet id aa26012; 21 Apr 85 9:54 EST Date: Sat, 20 Apr 85 15:33:46 est From: Will Clinger Received: by iuvax.UUCP; id AA04306; Sat, 20 Apr 85 15:33:46 est To: scheme@mit-mc.ARPA Subject: lexical syntax for numbers A proposed lexical syntax for Scheme numbers. The draft of the Revised Revised Report did not specify a lexical syntax for numbers. This proposal is consistent with the draft but it incorporates a large number of rather arbitrary decisions that are likely to be controversial. Feel free to controverse. Actually there are two proposals. The first proposal is for full (optional) Scheme and the second is for essential Scheme. (Implementations would be free to implement anything in between or to extend the syntax so long as the extended syntax didn't conflict with the optional syntax, right?) x* means zero or more occurrences of x. Spaces never appear inside a number, so all spaces in the syntax are for legibility. is the empty string. bit --> 0 | 1 oct --> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 dit --> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 hit --> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | d | e | f | A | B | C | D | E | F radix2 --> #b | #B radix8 --> #o | #O radix10 --> | #d | #D radix16 --> #h | #H exactness --> | #i | #I | #e | #E precision --> | #s | #S | #l | #L prefix2 --> radix2 exactness precision | radix2 precision exactness | exactness radix2 precision | exactness precision radix2 | precision radix2 exactness | precision exactness radix2 prefix8 --> radix8 exactness precision | radix8 precision exactness | exactness radix8 precision | exactness precision radix8 | precision radix8 exactness | precision exactness radix8 prefix10 --> radix10 exactness precision | radix10 precision exactness | exactness radix10 precision | exactness precision radix10 | precision radix10 exactness | precision exactness radix10 prefix16 --> radix16 exactness precision | radix16 precision exactness | exactness radix16 precision | exactness precision radix16 | precision radix16 exactness | precision exactness radix16 sign --> | + | - suffix --> | e d d* | E d d* | e - d d* | E - d d* ureal --> prefix2 bit bit* #* suffix | prefix2 bit bit* #* / bit bit* #* suffix | prefix2 . bit bit* #* suffix | prefix2 bit bit* . bit* #* suffix | prefix2 bit bit* #* . #* suffix | prefix8 oct oct* #* suffix | prefix8 oct oct* #* / oct oct* #* suffix | prefix8 . oct oct* #* suffix | prefix8 oct oct* . oct* #* suffix | prefix8 oct oct* #* . #* suffix | prefix10 dit dit* #* suffix | prefix10 dit dit* #* / dit dit* #* suffix | prefix10 . dit dit* #* suffix | prefix10 dit dit* . dit* #* suffix | prefix10 dit dit* #* . #* suffix | prefix16 hit hit* #* suffix | prefix16 hit hit* #* / hit hit* #* suffix | prefix16 . hit hit* #* suffix | prefix16 hit hit* . hit* #* suffix | prefix16 hit hit* #* . #* suffix real --> sign ureal number --> real | real + ureal i | real - ureal i | real @ real Some consequences of this syntax are reported below. Please keep in mind that when I say a syntax is "illegal", I mean that it is not generated by the syntax above and is therefore not a number in (full optional) Scheme. The syntax could represent a legal symbol or even a legal number in an extended Scheme. Consequences: Exponents are always in base 10. Exponents may not have explicit + signs. e+4 is illegal 1E+3 is illegal #d+3 is illegal 4-+5i is illegal 4#.5 is illegal #d5/#o10 is illegal -4/-5 is illegal #o5/10 is .625 #s4 is legal #s#i#b.10##-#x#L#Ea.bi is legal The proposal for essential Scheme: dit --> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 sign --> | + | - number --> sign dit dit* Question about exactness: In an implementation that doesn't keep track of exactness, the exact? predicate should always return #!false. Right? William Clinger willc@indiana after 1 May: willc@tektronix  Received: from csnet-relay by MIT-MC.ARPA; 21 APR 85 09:52:11 EST Received: from indiana by csnet-relay.csnet id a026012; 21 Apr 85 9:54 EST Date: Sat, 20 Apr 85 15:28:35 est From: Will Clinger Received: by iuvax.UUCP; id AA04262; Sat, 20 Apr 85 15:28:35 est To: scheme@mit-mc.ARPA Subject: if, mapcar, mapc, ... This note proposes a few significant changes and corrections to the draft of the "Revised Revised Report". Let me know if I have misread the consensus. I am moving to Oregon in two weeks. I expect June will arrive before I have finished typesetting this thing. William Clinger ---------------------------------------------------------------- Following the Algol 60 report, I propose that all attendees at the Brandeis workshop be listed as authors. The acknowledgements will list other participants in the discussion over the network, and I will sign an editor's note accepting blame for errors in transcription and interpretation. Chris Hanson's revised report on strings will be incorporated. In resonse to a few complaints, I would like to replace the last paragraph in Section I.0 ("Brief history of Scheme") with: Scheme shares with Common Lisp [\ref] the goal of a core language common to several implementations. Scheme differs from Common Lisp in its emphasis upon simplicity and function over compatibility with older dialects of Lisp. To allow for less error checking in compiled code, in the discussion of variable references in section II.1 "An error is signalled if {\it variable} is unbound" should read "It is an error if {\it variable} is unbound". Two-armed if will change from essential to optional status. In the discussion of cond, "The special keyword {\tt else} may be used..." should read "The keyword or variable {\tt else} may be used...". define! and defrec! will be flushed altogether. The rationale for set! will be flushed. In the header line for backquote, "macro special form" should be "special form". The initial value for nil (if it is provided at all) should be #!null rather than #!false. Several people have asked that append! not be allowed to side effect its last argument, so unless there is opposition that change will be made. See my separate note for the proposed lexical syntax of numbers and some questions that I have concerning numbers. There is a problem with object-hash and object-unhash. People might want to ask themselves whether object-unhash is really useful. The following scenario will be added to the rationale: >>>(define x (cons 0 0)) x >>>(object-hash x) 77 >>>(set! x 0) ... >>>(gc) ; garbage collection occurs for some reason ... >>>(object-unhash 77) ??? ; ill-defined: #!false or (0 . 0) The name of mapcar will be changed to map. (There seems to be a fair consensus for this.) The name of mapc will be changed to for-each. (There seems to be a fair consensus that it should be changed, and for-each seems to me to be the best name that has been proposed. At any rate it is the only name that has been proposed that has not drawn objections.) call-with-current-continuation is essential. That it was not so indicated in the draft was a gross blunder. The introductory material for call-with-current-continuation will be moved to the rationale. The last sentence of the rationale, which begins "Some people think...", will be changed to read "Although the name is descriptive, some people feel it is too long and have taken to calling the procedure call/cc". The original explanation of call-with-input-file and call-with-output-file allows the port to be closed on any throw. This makes it impossible to use escape procedures in a portable manner. The sentence beginning "If the current continuation ever changes in such a way as to make it doubtful that the procedure will return..." will therefore change to "The implementation must not close the port unless it can prove that the port will never again be used for a read or write operation." eof? will be renamed eof-object? listen? will be renamed char-ready?. (Everyone agreed that the name should be changed, but there was little agreement on the new name. No one seemed to care very much either.) It will be required that if char-ready? returns #!true, then the next read-char operation on the port must not hang, and it will be pointed out that this has implications for the way that rubout handlers are written. It will be required that the load procedure does not affect the values returned by current-input-port and current-output-port. There are many small improvements and corrections to be made also, but I won't bore you with them. (The above should suffice.)  Received: from MIT-OZ by MIT-MC via Chaosnet; 10 APR 85 04:06:27 EST Date: Wed, 10 Apr 1985 04:07 EST Message-ID: From: CPH%MIT-OZ@MIT-MC.ARPA To: Perry Wagle Cc: Scheme@MIT-MC Subject: First Class Environments and Their Extenders In-reply-to: Msg of 9 Apr 1985 02:35-EST from Perry Wagle Why did your message get sent to this mailing list? I think that there must have been some mistake as it seems to be inappropriate to the purpose of the mailing list, which is a forum for the Scheme report proposal.  Received: from csnet-relay by MIT-MC.ARPA; 9 APR 85 09:37:06 EST Received: from indiana by csnet-relay.csnet id a001256; 9 Apr 85 9:36 EST Date: Tue, 9 Apr 85 02:35:36 est From: Perry Wagle Received: by iuvax.UUCP; id AA15319; Tue, 9 Apr 85 02:35:36 est To: scheme%indiana.csnet@csnet-relay.arpa Subject: First Class Environments and Their Extenders My ATN (Augmented Transition Network) is a macro that makes a grammar into an object. This object (currently) is a closure taking one argument that is evaluated in an environment where each state has been transformed into a function of (currently) five arguments using letrec. Roughly: (define-atn my-atn state2 (state1 arc1 arc2) (state2 arc3 arc4)) is transformed to: (letrec ((state1 (lambda (...) (nexus arc1 arc2))) (state2 (lambda (...) (nexus arc3 arc4))) ) (define! my-atn (lambda (*sentence*) (state2 ...)))) where the "..." means "fill in the blank". NEXUS is a macro that determines the evaluation order of the arcs (depth-first, breadth-first, parallel, etc). One problem with this is that if I have a large ATN (with say 100 states, and an average branching factor of 5), the compile can take a significant amount of time, even if I have a VAX 780 all to myself at 3AM. How do you debug the grammar. Well, first you find the state in which it failed, you modify the source code of the grammar, and then you recompile the ENTIRE GRAMMAR. Yuck! The standard solution to this in the literature of ATNs (and AI in general) seems to be to define each state as a global-binding, and not to worry about trying to package them all up in objects. I don't like this because its not modular -- the inards of each compiled atn-grammar is sprawled out everywhere. I have several different atn-grammars, and I don't want to worry about them interfering with one another. The letrec method is a nice method, and I would like to make it work. My current view of what I think I want is this: That letrec in the right-hand-side of the transformation at the beginning of this note defined a new rib (MY_RIB) in front of the "current" environment (OLDENV) creating a new environment (MY_ENV). I want to rebind variables in MY_RIB to expressions EVALuated in MY_ENV, and I want to extend MY_RIB with new variables bound to the evaluations of expressions EVALuated in MY_ENV (with MY_ENV already extended by the new variable (which is initially bound to something like '#!unspecified', then rebound to the new value -- that is, normal letrec semantics). I could then create functions (macros?) like 'editstate', 'addstate', 'trace-state', 'untrace-state', etc. that would do the obvious things to a particular compiled-atn-grammar. This way, I only have to recompile the state, not the entire grammar. Put another way, I can modify the grammar interactively rather than in batch mode like I am now. PS -- I designed it this way, because I heard that first class environments were on their way, and I thought it would eventually work the way I wanted it too. But apparently now, the idea is that you should ONLY be able to extend the LAST rib in the rib sequence. Seeing as how I want to extend the second to the last rib (and two is an arbitrary number), and there being a number of programming technique reasons for putting new ribs between me and the second to the last rib, I have to object and call for a facility something like 'call-with-current-rib-set!' (where set! is rebind and extend as one), and I need to EVAL (compile, execute) in the proper environment, MY_ENV, which is not necessarily equal to the current one, and is procured with something like 'call-with-current-environment'. 'call/c_rib' is not necessarily incompatable with 'call/c_env' (as I almost thought) as I can: (let ((MY_RIB (call/c_rib (lambda (x) x))) (MY_ENV (call/c_env (lambda (x) x))) ) (some function of MY_RIB and MY_ENV here) ) As long as I can grab a rib extender, and EVAL in a environment other than the current one, everything is fine. The call/c_rib and call/c_env is just illustration. As I think that this generalizes to objects (modules), I don't think calls to rewrite my ATN are in order unless they make the same generalization.  Received: from MIT-OZ by MIT-MC via Chaosnet; 2 APR 85 03:05:44 EST Date: Tue, 2 Apr 1985 03:05 EST Message-ID: From: CPH%MIT-OZ@MIT-MC.ARPA To: Chris Haynes Cc: scheme@MIT-MC.ARPA Subject: CPH reply on DEFINE In-reply-to: Msg of 31 Mar 1985 20:08-EST from Chris Haynes My apologies for the rather careless wording of my earlier message. What I wanted to say was: if SET! extends the "global" environment, then that environment has become special in that it is the ONLY environment that can be extended by interactive definition. This would seem to preclude the existence of many such environments. DEFINE eliminates the problem, because it specifies, very precisely, the environment in which the name is bound.  Date: 1 April 1985 23:46-EST From: Jonathan A Rees Subject: CPH reply on DEFINE To: cth%indiana.csnet @ CSNET-RELAY cc: SCHEME @ MIT-MC In-reply-to: Msg of Sun 31 Mar 85 20:08:00 est from Chris Haynes Date: Sun, 31 Mar 85 20:08:00 est From: Chris Haynes We were simply debating whether SET! should be required to extend the global environment if its identifier is unbound (or equivalently, have everything bound in the global environment to begin with). This would make DEFINE unessential, though it might still be optional. It has nothing to do with multiple environments for incremental definition, except that MIT uses DEFINE for both purposes. I think that people at MIT and at Yale generally believe that SET! should NOT cause the variable assigned to become bound in ANY environment (global or otherwise) if it wasn't already. Although MIT Scheme's idea of environments argues for this position, there are these other reasons: (1) taste - who wants to see all those side-effects all over the place?; (2) one form one function - DEFINE binds, SET! updates. While I appreciate your desire to simplify things, DEFINE and SET! are viewed here as two totally different operations, so I think they should remain separate & required, as we agreed last fall. Jonathan  Received: from csnet-relay by MIT-MC.ARPA; 31 MAR 85 23:42:19 EST Received: from indiana by csnet-relay.csnet id ad16620; 31 Mar 85 23:29 EST Date: Sun, 31 Mar 85 20:08:00 est From: Chris Haynes Received: by iuvax.UUCP; id AA02544; Sun, 31 Mar 85 20:08:00 est To: scheme@mit-mc.ARPA Subject: CPH reply on DEFINE > CPH%MIT-OZ@MIT-MC.ARPA / 9:33 am Mar 30, 1985 ****/ > > It seems that the ability to have many > different environments in which to perform incremental definitions has > been consistently overlooked by almost everyone except MIT Scheme and > T. Anyone who has ever tried to program a BIG system, and by that I > mean something over 500-1000 pages of code, knows that this kind of > packaging is **ESSENTIAL**!! So please don't try to take this away. We grant the importance of such a facility, and are not trying to take it way; but there is no concensus on how to provide such a facility, so it is too soon to standardize on one. (Similarly, syntactic extensions are **ESSENTIAL** to the kind of thing we do here; but it is also too soon to standardize on a syntactic extension mechanism.) We were simply debating whether SET! should be required to extend the global environment if its identifier is unbound (or equivalently, have everything bound in the global environment to begin with). This would make DEFINE unessential, though it might still be optional. It has nothing to do with multiple environments for incremental definition, except that MIT uses DEFINE for both purposes. Chris Dan  Received: from csnet-relay by MIT-MC.ARPA; 31 MAR 85 23:42:15 EST Received: from indiana by csnet-relay.csnet id ac16620; 31 Mar 85 23:29 EST Date: Sun, 31 Mar 85 20:07:15 est From: Chris Haynes Received: by iuvax.UUCP; id AA02538; Sun, 31 Mar 85 20:07:15 est To: scheme@mit-mc.ARPA Subject: JINX reply on RECUR, DEFINE, ... > Recursion for me (and most of the MIT-Scheme people) is a > property of a process, not a feature of the particular syntax with > which it was expressed. We agree that the distinction between process and program is fundamental. But RECUR (Mitch's comment that RECURSE isn't English is well taken) is at least expressive of the program (and has the pedagogic virtue of emphasizing that program recursion expresses both recursion and iteration of processes). Local recursion (or iteration) is too fundamental to be subsumed by LET, which expresses local binding, *not* recursion (or iteration). We like Mitch's LABEL suggestion much better than named LET, but prefer RECUR. > SET! has too many connotations of side effects and I'm not willing > to accept it as the primary definition mechanism. > > I don't view definition as a side effect. Its interactive > implementation involves one, in the same way that the interpreted > implementation of LETREC involve side effects, only because we don't > know how to do it any better... > > Purists in MIT-Scheme advocate for DEFINE to signal an error when > attempting to redefine an already existing identifier in a given > environment... Your thorough justification of MIT's rationale is appreciated. If DEFINE could not be used to redefine existing bindings, we would have no problem with it. If the standard said that some systems may not permit such redefinitions, we'd feel better about DEFINE being essential. (Though most systems would allow redefinitions to ease reloading, as you indicated.) Your analogy with LETREC is not as close as you make it seem, for all code within the scope of a LETREC is known at compile time, and the same is not true for top level bindings. (This is also the root of the problem we have with the use of DEFINE as a substitute for LETREC.) LETREC bindings are always new, never mutations of existing bindings. > I'm willing to accept them in the standard as long as the CELL type > is not required to be disjoint from PAIR (in the same way that > CHARACTER is not required to be disjoint from STRING or INTEGER)... > > If they are adopted I suggest the following names: > > (MAKE-CELL VALUE) returns a CELL object with initial content VALUE. > > (CELL-CONTENT CELL) returns the content of cell CELL. > > (SET-CELL-CONTENT! CELL NEW-VALUE) makes the new content of CELL be > NEW-VALUE. We don't mind lack of disjointness. The names you propose are better than none at all, but not our favorites (here we go again...); we use them a lot (sometimes more than explicit cons cells) and would like shorter names. BOX and REF have the advantage that UNBOX and DEREF make sense, and SET-{CELL|BOX|REF}! is enough for mutation. We don't have to say CONS-CONTENT, or SET-CONS-CONTENT!. We know everyone has had about enough of these name debates, but they have served the useful purpose of clarifying our rationales. We appreciate Johnathan's restatement of the principle that if we can not agree after making our rationales clear, then the standard should try to avoid mentioning the points of contention. This indicates that neither named LET nor RECUR be mentioned. DEFINE could stay, since that seems to be the concensus, but it would be nice if its redefinition capability were optional. Chris Dan  Received: from csnet-relay by MIT-MC.ARPA; 31 MAR 85 23:42:10 EST Received: from indiana by csnet-relay.csnet id ab16620; 31 Mar 85 23:28 EST Date: Sun, 31 Mar 85 16:22:40 est From: Chris Haynes Received: by iuvax.UUCP; id AA03507; Sun, 31 Mar 85 16:22:40 est To: purdue!ihnp4!linus!ramsdell@mitre-bedf Subject: REC and LETREC Cc: scheme@mit-mc.ARPA > linus!ramsdell@mitre-bedf / 11:41 pm Mar 29, 1985 ****/ > > An English name for REC is PROCEDURE. After all, that is > what it returns. By the way, is the predicate PROCEDURE? > missing? REC may not return a procedure, as in (rec foo (cons exp (lambda () ...foo...))). I think we agreed to omit PROCEDURE?, since it doesn't do much good to know something is a procedure (without knowing how many args it wants, whether it ever returns, etc.). > Could some one explain why LETREC is preferable to LABELS? LETREC is what it seems, a recursive variant of LET. LABELS? only expresses that it has something to do with naming (labeling) things. Chris  Date: 31 March 1985 21:34-EST From: Jonathan A Rees Subject: READ-CHAR-READY? To: SCHEME @ MIT-MC In-reply-to: Msg of 29 Mar 1985 1437-CST from David Bartley I have a little trouble reading READ-CHAR-READY? ; it sounds like it ought to be an imperative (read a ready character), until you get to the question mark, and then it's hard to know what to think. How about something that sounds like it's asking a question? Something like: WOULD-THE-READ-CHAR-PROCEDURE-BLOCK-IF-I-CALLED-IT? only shorter. How about INPUT-AVAILABLE? or IMMEDIATELY-READABLE? or READABLE-WITHOUT-BLOCKING? or something like that. Does anyone else like INPUT-AVAILABLE? ? This is what it was always supposed to be called in Yale Scheme, except that it never got implemented. Jonathan  Date: 31 March 1985 21:22-EST From: Jonathan A Rees Subject: REC and LETREC To: JDR @ MIT-MC cc: SCHEME @ MIT-MC Date: 29 Mar 1985 18:36:48-EST From: linus!ramsdell at Mitre-Bedford By the way, is the predicate PROCEDURE? missing? It is intentionally missing; the decision to omit it was the outcome of a discussion at the workshop. As I remember, it was decided that it was unnecessary, and certain people (me) thought it was possibly meaningless and/or harmful. Could some one explain why LETREC is preferable to LABELS? It was decided that a way to introduce local recursive definitions was absolutely necessary; DEFINE of course being out of the question, there was a heated debate between the names LABELS and LETREC, the LABELS people arguing for English words and respect for the 1978 Revised Report and Common Lisp, and the LETREC people arguing for something more descriptive and allegedly pervasive in FP literature. The LABELS camp (me) was overwhelmed, so LETREC it is. Let us be happy there was any agreement at all. Jonathan  Received: from MIT-OZ by MIT-MC via Chaosnet; 30 MAR 85 03:54:03 EST Date: Sat, 30 Mar 1985 03:53 EST Message-ID: From: CPH%MIT-OZ@MIT-MC.ARPA To: David Bartley Cc: Scheme@MIT-MC.ARPA Subject: The revised revised Report In-reply-to: Msg of 29 Mar 1985 15:37-EST from David Bartley Regarding your most recent set of (about 15) suggestions: I agree completely with every single one of them. Thank you.  Received: from MIT-OZ by MIT-MC via Chaosnet; 30 MAR 85 03:47:23 EST Date: Sat, 30 Mar 1985 03:46 EST Message-ID: From: CPH%MIT-OZ@MIT-MC.ARPA To: Chris Haynes Cc: scheme@MIT-MC.ARPA Subject: ITERATE, DEFINE and CELLs In-reply-to: Msg of 29 Mar 1985 08:49-EST from Chris Haynes Date: Friday, 29 March 1985 08:49-EST From: Chris Haynes > How am I supposed to define things if both DEFINE! and DEFINE > go away? With SET!, provided one assumes that all identifiers are initially bound in the global environment, or that SET! can extend the global environment. With the exception of MIT's Scheme, this is what existing systems do. If MIT is unwilling to change this, then we are reluctantly stuck with DEFINE. This is a terrible idea. It seems that the ability to have many different environments in which to perform incremental definitions has been consistently overlooked by almost everyone except MIT Scheme and T. Anyone who has ever tried to program a BIG system, and by that I mean something over 500-1000 pages of code, knows that this kind of packaging is **ESSENTIAL**!! So please don't try to take this away.  Received: from csnet-relay by MIT-MC.ARPA; 30 MAR 85 00:35:54 EST Received: from ti-csl by csnet-relay.csnet id ac08681; 30 Mar 85 0:24 EST Date: 29 Mar 1985 1437-CST From: David Bartley Subject: RE: The revised revised Report To: Scheme@mit-mc.ARPA cc: Bartley%ti-csl.csnet@csnet-relay.arpa Received: from csl60 by ti-csl; Fri, 29 Mar 85 15:00 CST Here are my votes on some of the issues swirling around the Scheme World these days... -- I strongly prefer the term "port." -- I prefer the names CAR and CDR to anything else I've heard. I am partly motivated by the desire to woo others away from inferior forms of Lisp; avoiding such changes may help. -- I prefer MAP and WALK to MAPCAR and MAPC. Common Lisp's MAP is a generalization of MAPCAR, so I don't see any problem there. WALK is more meaningful than MAPC. WALK should be optional; MAP may be either essential or optional. -- Both IF and WHEN are worthwhile. "One-armed" IF and WHEN should both be optional. I use WHEN exclusively for side effects and the choice of the name helps clarify my intent. I don't like UNLESS, because it adds nothing new, but making it optional is OK by me. -- I'd like to modify EQV? slightly to align it with Common Lisp's EQL. I believe the only difference is that EQL "does the right thing" for character data objects as well as for numbers. Does anyone oppose this? -- I am worried about PRIMITIVE-SYNTAX-EXPAND. It "feels" like too little, too late in the game. I'd rather allow time for this issue to gel further. -- It has been suggested that LAMBDA be the only special form. Unless we tackle the subject of macros in detail, I can't imagine subcategorizing special forms into "real" special forms and "other" special forms, some of which may be macros. -- I prefer named LET to ITERATE or RECURSE. ITERATE is inadequate, while RECURSE is poor English. If we can't agree, lets make named LET and perhaps one of the alternatives both optional (as Dan and Chris suggest). -- Let's drop LIST?. It's both expensive and unimportant. Make it optional if you'd like. -- I like READ-CHAR-READY?. -- I don't like READ-READY?. Its implementation would seem to require buffering all of the characters going into a READ just in case we find they don't add up to an entire parsable object. HUNG? has the same problem. -- I agree that = and =? shouldn't both be essential. I'm willing to switch to the '?' forms if necessary, although I prefer the others for compatibility with the Abelson and Sussman book and with Common Lisp. -- Let's drop #!NULL. #!FALSE and () suffice. -- I'm willing to lose DEFINE! and DEFREC!, as Chris and Dan suggest. -- I favor retaining NUMBER?. -- OBJECT-HASH and OBJECT-UNHASH should be optional. -- I prefer REF over CELL (and CELL over BOX). It should be optional. In our work, we are also concerned about locatives and forwarding pointers, so it isn't clear to us that REF alone is interesting. Regards, David Bartley -------  Received: from csnet-relay by MIT-MC.ARPA; 30 MAR 85 00:35:45 EST Received: from ti-csl by csnet-relay.csnet id aa08681; 30 Mar 85 0:24 EST Date: 29 Mar 1985 1355-CST From: David Bartley Subject: NIL vs () To: Scheme@mit-mc.ARPA cc: Bartley%ti-csl.csnet@csnet-relay.arpa Received: from csl60 by ti-csl; Fri, 29 Mar 85 14:27 CST Thanks to JAR and GJC, I have a clearer idea of how to avoid the problem I've raised recently. This has been one of those things that could be cleared up in 10 minutes in person, but has been beset with terminology problems. I withdraw my request. Regards, David Bartley -------  Received: from csnet-relay by MIT-MC.ARPA; 29 MAR 85 19:31:38 EST Received: from brandeis by csnet-relay.csnet id ab07489; 29 Mar 85 19:17 EST Received: by brandeis.ARPA (4.12/4.7) id AA03530; Fri, 29 Mar 85 13:37:18 est Date: 29 Mar 1985 13:30-EST From: mw%brandeis.csnet@csnet-relay.arpa In-Real-Life: Mitchell Wand,faculty Subject: Other names for ITERATE To: scheme@mit-mc.ARPA Message-Id: <480969029/mw@brandeis> How about LABEL? Except for its (obsolescent) use in Lisp and in imperative languages, it has no other connotations, and it corresponds nicely to the purported denotational semantics. Furthermore, each of those uses corresponds to a recursive declaration of some sort. Thus: (label foo ((x 0)(y 1)(k (lambda (x) x))) blah) BTW, "recursion" is the noun form of the verb "recur" (to occur again). [As vision is the noun form of the verb "view"]. So if you verb "recursion", you should get "recur", not "recurse", as in: "this function recurs (not recurses) on the cdr of its argument." -- Mitch  Received: from mitre-bedford by MIT-MC.ARPA; 29 MAR 85 18:37:51 EST Date: 29 Mar 1985 18:36:55-EST From: linus!ramsdell@Mitre-Bedford Received: by linus.UUCP (4.12/4.7) id AA19122; Fri, 29 Mar 85 09:19:38 est Date: Fri, 29 Mar 85 09:19:38 est From: linus!ramsdell (John D. Ramsdell) Message-Id: <8503291419.AA19122@linus.UUCP> To: bccvax!scheme@mit-mc.arpa Subject: LIST? Is it agreed that LIST? should be included in the report and that is should be defined as per Bill Rozas? Eg. The predicate that returns true to objects generated by LIST. John  Received: from mitre-bedford by MIT-MC.ARPA; 29 MAR 85 18:37:03 EST Date: 29 Mar 1985 18:36:51-EST From: linus!ramsdell@Mitre-Bedford Received: by linus.UUCP (4.12/4.7) id AA19092; Fri, 29 Mar 85 09:14:58 est Date: Fri, 29 Mar 85 09:14:58 est From: linus!ramsdell (John D. Ramsdell) Message-Id: <8503291414.AA19092@linus.UUCP> To: bccvax!scheme@mit-mc.arpa Subject: rationalized names How about adding a section on naming conventions, much like the so named section in the T manual. Agreeing one these conventions would greatly increase program portability. John  Received: from mitre-bedford by MIT-MC.ARPA; 29 MAR 85 18:36:51 EST Date: 29 Mar 1985 18:36:48-EST From: linus!ramsdell@Mitre-Bedford Received: by linus.UUCP (4.12/4.7) id AA19046; Fri, 29 Mar 85 09:08:34 est Date: Fri, 29 Mar 85 09:08:34 est From: linus!ramsdell (John D. Ramsdell) Message-Id: <8503291408.AA19046@linus.UUCP> To: bccvax!scheme@mit-mc.arpa Subject: REC and LETREC An English name for REC is PROCEDURE. After all, that is what it returns. By the way, is the predicate PROCEDURE? missing? Could some one explain why LETREC is preferable to LABELS? If the name LABELS is so bad, let me suggest alternatives: LABEL MARK TAG NOTE GIVE SUPPLY PROVIDE ; from the ITERATE suggestion. ALLOW John  Received: from csnet-relay by MIT-MC.ARPA; 29 MAR 85 14:41:06 EST Received: from indiana by csnet-relay.csnet id a006294; 29 Mar 85 14:32 EST Date: Fri, 29 Mar 85 12:02:05 est From: Will Clinger Received: by iuvax.UUCP; id AA03416; Fri, 29 Mar 85 12:02:05 est To: scheme@mit-mc.ARPA Subject: names With respect to special forms vs procedures, IF vs IF/WHEN/UNLESS, LET vs LET/ITERATE, and feeping creaturism, please remember that the keywords of special forms may be reserved in some implementations and thus cannot be used as variables in portable code. When I was a Cobol programmer, it occurred to me that the main reason that experienced Cobol programmers were so much more productive than inexperienced programmers was that they didn't have to flip through the list of several hundred reserved words every time they wanted to name a variable. Have pity on the poor beginner. I could probably keep fifty de facto reserved words in mind as I program, though there are more important things that I ought to keep in mind instead. Beginners, however, are likely to have trouble remembering the twenty or so we already have. One of the glories of Scheme is that it gives T (and NIL) back to the programmer. At last I can use T as a temporary or to stand for time! Now they threaten to take away WHEN and ITERATE. Arise, programmers, and defend your namespace! P.S. I am in sympathy with the idea of making one-armed IF optional rather than essential. The one-armed IF that acts like material conditional looks like a feeping creature to me. If our strongest disagreements have to do with procedure names, then we've made real progress. I think MAP is much nicer than MAPCAR, and I would like to see it adopted so we can keep up with the rest of the functional programming community. MAPC is utterly random, but while WALK is better it isn't obviously the right thing and it doesn't have MAPC's tradition. Anyone for STEP, STOMP, JOG, TIPTOE, PROMENADE, TRAVERSE, or VISIT ? William Clinger  Date: 29 March 1985 14:09-EST From: Jonathan A Rees Subject: ITERATE, DEFINE, WHEN, cells, etc. To: SCHEME @ MIT-MC In-reply-to: Msg of Fri 29 Mar 85 08:49:59 est from Chris Haynes Why are people so hot to change the report? Many of these questions (such as that of ITERATE and its alternatives) were discussed last fall, and we agreed that we couldn't agree, so the features got omitted. The report seems mostly fine to me, and given how strongly people feel about iteration primitives, DEFINE, and everything else, I don't think it's worth rocking the boat again. Perhaps not everyone who is participating in current discussions has an understanding of how strong the disagreements are, or perhaps they forget that we agreed that controversial features are to be ommitted, unless their absence gravely compromises the ability to express programs at all. If anything, the report should be pruned, not expanded. It is already much larger than most of us would like it to be. My suggestion that (IF A B) be changed from essential to optional was an attempt to remove something I thought was both redundant and controversial, and I thought it a reasonable suggestion, for symmetry with the optionality of (DEFINE (F X) Y) ; the fact that there's been so much argument just supports my proposal that it be removed from the essential dialect! In any case, I think we should concern ourselves with inconsistencies and redundancies, not with questions which were settled (by agreeing to not settle them) long ago. The most serious omissions (numbers, strings, and I/O) have already been addressed. These other features are simply not in that class (although I suppose that this meta-point is what the arguments are about, in part). Please, be conservative, and consider that if you are likely to meet implacable opposition, it is probably better to be quiet. If you have a new proposal which is just so good that you think everyone will like it, by all means submit it, but remember that most of us are fanatical minimalists, and have incompatible measures of minimality. Form models of other peoples' viewpoints and modify your speeches accordingly. Please forgive me for this outburst, but I felt things were getting a bit out of hand. This is not to say that many recent messages have not been reasonable and good. Jonathan  Received: from MIT-OZ by MIT-MC via Chaosnet; 29 MAR 85 12:57:18 EST Date: 29 Mar 1985 12:56 EST (Fri) Message-ID: From: Bill Rozas To: GJS%MIT-OZ@MIT-MC.ARPA Cc: scheme@MIT-MC Subject: WHEN, UNLESS, RECURSE, et. al. In-reply-to: Msg of 29 Mar 1985 11:47-EST from GJS (4) Messages about WHEN, UNLESS, RECURSE, etc should not be multiplied beyond necessity.  Received: from MIT-OZ by MIT-MC via Chaosnet; 29 MAR 85 12:49:40 EST Date: 29 Mar 1985 12:49 EST (Fri) Message-ID: From: Bill Rozas To: Chris Haynes Cc: scheme@MIT-MC.ARPA Subject: ITERATE, DEFINE and CELLs In-reply-to: Msg of 29 Mar 1985 08:49-EST from Chris Haynes > Recursion subsumes iteration, so RECURSE is a good name even if used for > simple iteration, but ITERATE isn't a good name when its use is not > tail-recursive. The *disadvantage* of named let is that its name *does* have > connotation, namely local binding and *not* recursion. Our desire to keep > down the number of keywords should not be at the expense of such confusion. I disagree. Recursion for me (and most of the MIT-Scheme people) is a property of a process, not a feature of the particular syntax with which it was expressed. If forms like DO loops are allowed, there is no syntactic recursion and no real recursion. One can easily imagine some dual special form which avoids syntactic recursion while developing a recursive process when evaluated (executed). RECURSE (recur) has too many such connotations for me since I view a procedure as a way of encapsulating a process, and the only interesting thing is the process, not its syntactic description. This distinction between procedure and process developed by it is very important, though irrelevant in most programming languages (without a notion of reduction vs. subproblem) but not in Scheme. It is further expounded in "Structure and Interpretation ..." section 1.2.1. > With SET!, provided one assumes that all identifiers are initially bound in > the global environment, or that SET! can extend the global environment. > With the exception of MIT's Scheme, this is what existing systems do. If MIT > is unwilling to change this, then we are reluctantly stuck with DEFINE. SET! has too many connotations of side effects and I'm not willing to acceptit as the primary definition mechanism. I don't view definition as a side effect. Its interactive implementation involves one, in the same way that the interpreted implementation of LETREC involve side effects, only because we don't know how to do it any better. The side effects do not exist for internal definitions. In the same way that the implementation of LETREC is incomplete (it can only find a fixed point in some cases, not all, and the others are disallowed or give an error), the implementation of DEFINE is incomplete. Its limitations are no worse than the limitations of LETREC, yet they meet considerable opposition, which I don't understand. Purists in MIT-Scheme advocate for DEFINE to signal an error when attempting to redefine an already existing identifier in a given environment. While conceptually this would be not only appropriate but logical, it has problems in an interactive system when people want to re-load some code after making some changes to it. It would be very cumbersome to have to change all DEFINEs into SET!s (though I sometimes think this should be the case). This is the only reason why we allow "re-definition" of identifiers (again interactive constraints). In a given program there should be no redefinitions (I believe that our "compiler" will complain, but I'm not sure since I never do it), and definition should occur before assignment or use. We even have a stylistic convention (unfortunately not rigidly adhered to) to aid the user in determining whether the definition is static (the value will not change), or we are only introducing an identifier for further assignment. In the latter case no initial value is given, thus (define foo) (set! foo 3) ; Initial value for foo, which will change Means something quite different from (define foo 3) ; Foo is supposed to be 3 This corresponds to LSET and DEFINE in T, but not imposed by the system. As you can see, in neither case does the definition conceptually involve a side-effect, since in the second it declares a constant, and in the first it declares an identifier which can be used in assignments. DEFINE is thus purely declarative, as opposed to SET! which is imperative. I believe this distinction is very important, and justifies the keeping of DEFINE (or some such declarative form). > They take up half as much space as pairs, and have many fundamental uses. > They aren't *absolutely* necessary, but then neither is most of Scheme. I'm willing to accept them in the standard as long as the CELL type is not required to be disjoint from PAIR (in the same way that CHARACTER is not required to be disjoint from STRING or INTEGER). There is actually a reason for this, I can see a few proposed memory (debugging) utility programs whose implementation depends on the fact that all pointer objects have at least 2 words of storage, which would not be the case for CELLS. If they are addopted I suggest the following names: (MAKE-CELL VALUE) returns a CELL object with initial content VALUE. (CELL-CONTENT CELL) returns the content of cell CELL. (SET-CELL-CONTENT! CELL NEW-VALUE) makes the new content of CELL be NEW-VALUE.  Received: from MIT-OZ by MIT-MC via Chaosnet; 29 MAR 85 11:49:46 EST Date: Fri, 29 Mar 1985 11:47 EST Message-ID: From: GJS%MIT-OZ@MIT-MC.ARPA To: scheme@MIT-MC Subject: WHEN, UNLESS, RECURSE, et. al. Rules for Scheme standards: (1) Entities should not be multiplied beyond necessity. -- William of Occam (2) Special forms should not be multiplied beyond necessity. (3) Rules for Scheme standard should not be multiplied beyond necessity.  Received: from csnet-relay by MIT-MC.ARPA; 29 MAR 85 10:59:38 EST Received: from unc by csnet-relay.csnet id a005294; 29 Mar 85 10:52 EST Received: by unc (4.12/4.7) id AA07930; Fri, 29 Mar 85 09:08:20 est Date: Fri, 29 Mar 85 09:08:20 est From: Kent Dybvig Message-Id: <8503291408.AA07930@unc> To: scheme@mit-mc.ARPA Subject: Re: Other names for ITERATE How about "spiral" or "cycle"? Spirals can loop back on themselves, or build up in a recursive fashion, and the name has no other connotation that I know of. last I heard, spiral was a word in the English language (though cdr, ref, cond and cons are not). Cycle is also a word but may have other connotations. (spiral f ((x l)) (if (null? l) '() (cons (car l) (f (cdr l))))) (cycle f ((x l)) (if (null? l) '() (cons (car l) (f (cdr l)))))  Received: from csnet-relay by MIT-MC.ARPA; 29 MAR 85 10:13:30 EST Received: from indiana by csnet-relay.csnet id aa05046; 29 Mar 85 10:05 EST Date: Fri, 29 Mar 85 08:49:59 est From: Chris Haynes Received: by iuvax.UUCP; id AA00974; Fri, 29 Mar 85 08:49:59 est To: scheme@mit-mc.ARPA Subject: ITERATE, DEFINE and CELLs > RECURSE has the same bug that ITERATE has. The name implies a > certain behaviour which the process it develops may not conform to. In > particular, I usually use named let for loops, but I also use it for > recursive procedures. The advantage of named let is that its name has > no connotations, and that it reduces the number of "reserved" > keywords. Recursion subsumes iteration, so RECURSE is a good name even if used for simple iteration, but ITERATE isn't a good name when its use is not tail-recursive. The *disadvantage* of named let is that its name *does* have connotation, namely local binding and *not* recursion. Our desire to keep down the number of keywords should not be at the expense of such confusion. > How am I supposed to define things if both DEFINE! and DEFINE > go away? With SET!, provided one assumes that all identifiers are initially bound in the global environment, or that SET! can extend the global environment. With the exception of MIT's Scheme, this is what existing systems do. If MIT is unwilling to change this, then we are reluctantly stuck with DEFINE. > While I agree that CELLs are nice, why are they neccesary? > They can easily be emulated with pairs, and any implementation which > really uses them can support them optionally. They take up half as much space as pairs, and have many fundamental uses. They aren't *absolutely* necessary, but then neither is most of Scheme. Chris Dan  Received: from csnet-relay by MIT-MC.ARPA; 29 MAR 85 01:49:09 EST Received: from unc by csnet-relay.csnet id a002999; 29 Mar 85 1:39 EST Received: by unc (4.12/4.7) id AA03857; Thu, 28 Mar 85 09:35:09 est Date: Thu, 28 Mar 85 09:35:09 est From: Kent Dybvig Message-Id: <8503281435.AA03857@unc> To: GJS%MIT-OZ@mit-mc.ARPA, scheme@mit-mc.ARPA Subject: Re: flames! (if (if p q) r s) Could of course be written: (if (if p q #!true) r s) "unless" is a handy special form, in addition to "when": (unless (zero? x) (set! x (/ 1 x))) would have to be written as: (when (not (zero? x)) (set! x (/ 1 x))) or (shudder): (if (not (zero? x)) (set! x (/ 1 x))) It has always bothered me that the "else" part was optional in the one-or-two-armed-if. Why can't the "then" part be optional as well? "when" and "unless" are clean, simple, and unambiguous. They should both be included in the standard.  Received: from MIT-OZ by MIT-MC via Chaosnet; 28 MAR 85 18:11:14 EST Date: 28 Mar 1985 18:10 EST (Thu) Message-ID: From: Bill Rozas To: linus!ramsdell@MITRE-BEDFORD.ARPA, scheme@MIT-MC.ARPA Subject: Other names for ITERATE In-reply-to: Msg of 28 Mar 1985 17:36-EST Thu 28 Mar 85 09:27:45 est from linus!ramsdell at Mitre-Bedford, linus!ramsdell (John D. Ramsdell) at Mitre-Bedford Why are you not willing to go one step further and notice ALLOW = LET?  Received: from mitre-bedford by MIT-MC.ARPA; 28 MAR 85 17:37:56 EST Date: 28 Mar 1985 17:36:55-EST From: linus!ramsdell@Mitre-Bedford Received: by linus.UUCP (4.12/4.7) id AA06997; Thu, 28 Mar 85 09:27:45 est Date: Thu, 28 Mar 85 09:27:45 est From: linus!ramsdell (John D. Ramsdell) Message-Id: <8503281427.AA06997@linus.UUCP> To: bccvax!scheme@mit-mc.arpa Subject: Other names for ITERATE Here are some synonyms for let that could be used as replacements for ITERATE. Notice they do not give any connotation on how the named function will be used. ALLOW PROVIDE ENTITLE FOSTER I think the following does looks pretty good. (ALLOW LOOP ((L L) (SUM 0)) (IF (NULL? L) SUM (LOOP (CDR L) (+ (CAR L) SUM)))) John  Received: from yale by MIT-MC.ARPA; 28 MAR 85 15:59:18 EST Received: by YALE-BULLDOG.YALE.ARPA; 28 Mar 85 15:45:05 EST (Thu) Message-Id: <8503282045.AA03847@YALE-BULLDOG.YALE.ARPA> Received: from YALE-RING by YALE-RES via CHAOS; Thu, 28 Mar 85 15:07:00 EST Subject: Re: IF and WHEN Date: Thu, 28 Mar 85 15:07:03 EST From: Paul Hudak To: Bill Rozas Cc: scheme@MIT-MC In-Reply-To: Bill Rozas , 28 Mar 1985 10:52 EST (Thu) Do you use EMACS? No. Perhaps I should, but I work on an Apollo. It occurred to me after sending the message that counting clauses in an IF is no different than counting them in any other special form, including bodies of LET, etc. Somehow it always seems worse for an IF, though, perhaps because people tend to write one-liners using IF. BTW, where does JINX%MIT-OZ@MIT-MC come from? And how does it relate to JINX@OZ.MIT which used to work but got rejected on my last message? Is SCHEME@MIT-MC the only relevant mailing-list for these discussions? -Paul  Received: from MIT-OZ by MIT-MC via Chaosnet; 28 MAR 85 11:04:12 EST Date: 28 Mar 1985 11:02 EST (Thu) Message-ID: From: Bill Rozas Subject: syntax-expand To: kmp@MIT-MC cc: scheme@MIT-MC I don't think you understood my message. I was not saying that we shouldn't have them, just that providing single level expansion (especially for syntax-expand in the presence of macros) may not be so easy. Since you left open the possibility for full expansion or single level, I vote for full expansion. [KMP: Sorry that you'll get this message 3 times, but I spazzed]  Received: from MIT-OZ by MIT-MC via Chaosnet; 28 MAR 85 10:53:13 EST Date: 28 Mar 1985 10:52 EST (Thu) Message-ID: From: Bill Rozas To: Paul Hudak Cc: scheme@MIT-MC Subject: IF and WHEN In-reply-to: Msg of 28 Mar 1985 10:02-EST from Paul Hudak Do you use EMACS? If so C-M-F (Lisp or SCheme Modes) does the job when you're confused. I didn't mean counting parens.  Received: from yale by MIT-MC.ARPA; 28 MAR 85 10:17:42 EST Received: by YALE-BULLDOG.YALE.ARPA; 28 Mar 85 10:04:59 EST (Thu) Message-Id: <8503281504.AA07856@YALE-BULLDOG.YALE.ARPA> Received: from YALE-RING by YALE-RES via CHAOS; Thu, 28 Mar 85 10:02:02 EST Subject: IF and WHEN Date: Thu, 28 Mar 85 10:02:05 EST From: Paul Hudak To: scheme@MIT-MC Cc: Invalid-Addresses: JINX@OZ.MIT.ARPA (?Invalid domain (host)) The issue of IF vs. WHEN came up when this mailing-list was first set up, at which time I responded in favor of WHEN. I'd like to say again why: when I read other people's code and come across an IF, I almost always search to see if there is a second arm. The bad thing about this is that if I don't see one I look harder, carefully matching parens, to make sure I didn't miss it. I don't buy the "indentation" argument, because one shouldn't trust other people to follow stylistic conventions. Simply from a readability standpoint, WHEN is a good idea, but at least we should require that IF have two arms. "Feeping creaturism" is not always bad... -Paul  Received: from SCRC-STONY-BROOK by MIT-MC via Chaosnet; 28 MAR 85 09:11:51 EST Received: from SCRC-RIO-DE-JANEIRO by SCRC-STONY-BROOK via CHAOS with CHAOS-MAIL id 204535; Wed 27-Mar-85 19:34:21-EST Date: Wed, 27 Mar 85 19:34 EST From: Kent M Pitman Subject: How to let macros work without defining what a macro is... To: JINX@OZ.MIT cc: Scheme@MIT-MC.ARPA In-Reply-To: Message-ID: <850327193441.0.KMP@RIO-DE-JANEIRO.SCRC.Symbolics.COM> Date: 27 Mar 1985 10:19 EST (Wed) From: Bill Rozas This assumes that all macros work at the s-expression level, which is not true in our implementation. The only way to provide both (especially syntax-expand) in all cases would be to fully reduce to s-code and then invert to s-expressions. This would imply full (recursive) expansion always. In our system macros translate from s-expressions to s-code. There are a few utilities, however, to emulate s-expression level macros built on top of this. But macros like COND, LET, etc are never translated at the s-expression level. No, you missed the point. You can implement these special forms however you want. What I'm proposing is something that takes forms which may be in an extended syntax and translates it into a standard syntax. eg, suppose the only extended forms in some dialect are WHEN and UNLESS. Regardless of how those are implemented (perhaps directly represented in S-code, perhaps translated to something else in S-code, whatever), all you have to provide is (literally) the following function: (DEFINE (PRIMITIVE-SYNTAX-EXPAND FORM) (COND ((ATOM FORM) FORM) ((EQ (CAR FORM) 'WHEN) `(COND (,(CADR FORM) ,@(CDDR FORM)))) ((EQ (CAR FORM) 'UNLESS) `(COND ((NOT ,(CADR FORM)) ,@(CDDR FORM)))) (T FORM))) You never have to call the function internally in the system. It simply has to be available for users who want to use it. It must be the responsibility of system maintainers to keep it up to date so that code-manipulating tools can be written which assume a fixed number of special forms and new special forms can later be added to standard Scheme which do not cause a need to rewrite those tools. Suppose that the only special forms we could agree on being standard were LAMBDA, QUOTE, and SETQ. Suppose that someone insisted that (IF X Y Z) had to be written (*IF (LAMBDA () X) (LAMBDA () Y) (LAMBDA () Z)) and *IF had to be a function, etc. Assume similar rewrites were suggested for other special forms. Now consider a user program in a particular dialect which had extra special forms (eg, DEFINE, COND, ...): (DEFINE (USED-FREE-IN EXP VAR TAIL?) (COND ((ATOM VAR) (EQ EXP VAR)) (FLAG (COND ((NULL EXP) NIL) ((USED-FREE-IN (CAR EXP) VAR NIL) T) (T (USED-FREE-IN (CDR EXP) VAR T)))) ((EQ (CAR EXP) 'LAMBDA) (COND ((MEMQ VAR (CADR EXP)) NIL) (T (USED-FREE-IN (CDDR EXP) VAR T)))) ((EQ (CAR EXP) 'SETQ) (OR (EQ EXP (CADR FORM)) (USED-FREE-IN (CADDR EXP) VAR NIL))) ((EQ (CAR EXP) 'QUOTE) NIL) (T (USED-FREE-IN EXP VAR T)))) If I didn't blow it, this program "works" on input which contains LAMBDA, SETQ, and QUOTE but does not work on input that contains COND, etc. It could, however, work on those, too, by simply making it say: (DEFINE (USED-FREE-IN EXP VAR TAIL?) (LET ((EXP (PRIMITIVE-SYNTAX-EXPAND EXP))) (COND ((ATOM VAR) (EQ EXP VAR)) (FLAG (COND ((NULL EXP) NIL) ((USED-FREE-IN (CAR EXP) VAR NIL) T) (T (USED-FREE-IN (CDR EXP) VAR T)))) ((EQ (CAR EXP) 'LAMBDA) (COND ((MEMQ VAR (CADR EXP)) NIL) (T (USED-FREE-IN (CDDR EXP) VAR T)))) ((EQ (CAR EXP) 'SETQ) (OR (EQ EXP (CADR FORM)) (USED-FREE-IN (CADDR EXP) VAR NIL))) ((EQ (CAR EXP) 'QUOTE) NIL) (T (USED-FREE-IN EXP VAR T))))) The user could write PRIMITIVE-SYNTAX-EXPAND himself, but he'd have to: (a) Make sure his understanding of the syntax equivalences was letter perfect (the system implementors have a better chance of getting this right) (b) Update the function every time the dialect changed. ie, adding a special form if PRIMITIVE-SYNTAX-EXPAND is not a system primitive function is an incompatible change, but is a compatible change if PRIMITIVE-SYNTAX-EXPAND is kept in synch with available special forms. It would be possible to have forms which were not translatable. They should simply be advertised as such and PRIMITIVE-SYNTAX-EXPAND should err when it sees them so that the user doesn't think he's winning when he's not. Does this make it clearer? -kmp  Date: 27 March 1985 20:26-EST From: Jonathan A Rees Subject: NIL, again To: Bartley%ti-csl.csnet @ CSNET-RELAY cc: SCHEME @ MIT-MC Date: 27 Mar 1985 1038-CST From: David Bartley Let me walk through an instance of the problem. Assume an existing Lisp in which ()==NIL. In Lisp, generate a list; the list will be "terminated" with the symbol NIL in the last CDR. Pass the list to a Scheme routine, which cdr's down it until it reaches -- what? Unless #!NULL==NIL, Scheme can't detect the end of the list with a simple EQ? test. Sharing of list data between the two languages is effectively ruled out. I don't agree that things are hopeless. I will risk repetition in hope of promoting understanding; my apologies if this message adds nothing to what I've already said. To implement Scheme on top of a Lisp which identifies #!NULL (i.e. (), false) with Lisp's symbol NIL, create a new, unique object; call it SCHEME:NIL. Use this object to implement "the" symbol NIL for Scheme. Use the object #!NULL, which in Lisp is a symbol and in Scheme is not, to represent both the empty list and false in Scheme. So you have the following equations: Lisp: Scheme: Symbol <=> Symbol-but-not-#!NULL SCHEME:NIL <=> The symbol NIL NIL, () <=> (), #!NULL, #!FALSE The phrase "the symbol NIL" is confusing. You need to say either "the Scheme symbol NIL" or "the Lisp symbol NIL". Lisp and Scheme may agree on what the empty list and false are, without agreeing on what "the symbol NIL" is. (define (symbol? x) (and x (or (eq? x 'nil) ;This code is read using the Scheme reader! (lisp:symbolp x)))) (define (symbol->string x) (cond ((eq? x 'nil) "NIL") ((not x) (error ...)) (else (lisp:symbol-name x)))) (define (string->symbol x) (cond ((string-equal? x "NIL") 'nil) (else (lisp:intern x *scheme-package*)))) (define (null? x) (lisp:null x)) In Common Lisp, these definitions need not be quite so verbose, if you arrange for the object SCHEME:NIL to be a Common Lisp symbol whose print name is "NIL" (not EQ to () !). I do not believe that you will run into trouble if you take this approach. Since the list terminator is the same between Lisp and Scheme, there is no problem sharing list structure. If you plan to use the Common Lisp reader to read Scheme expressions, then you are in a bit of trouble; Common Lisp allows some way to define octathorp read macros (so you can implement Scheme #!..., #I, etc.), but no way to influence the atom reader. But won't you have all sorts of trouble with numbers if you don't change the atom reader? I find it hard to believe that you would not be able to make the trivial required change (the addition of a single COND clause, no more than two lines of code!). If this patch is really impossible, and you cannot use a different reader (another tractable solution), then indeed I agree you will be forced to equate Scheme's NIL with (). I'm sure that would be fine as an interim solution. The symbol/false distinction is too useful, and likely to be too ingrained in programs, to permit deviation from it in the R.R. (although I agree that legalities are not at issue here). Jonathan  Received: from mitre-bedford by MIT-MC.ARPA; 27 MAR 85 19:44:51 EST Date: 27 Mar 1985 19:39:08-EST From: linus!ramsdell@Mitre-Bedford Received: by linus.UUCP (4.12/4.7) id AA25130; Wed, 27 Mar 85 10:45:05 est Date: Wed, 27 Mar 85 10:45:05 est From: linus!ramsdell (John D. Ramsdell) Message-Id: <8503271545.AA25130@linus.UUCP> To: bccvax!scheme@mit-mc.arpa Subject: Scheme names When is said that I thought one armed IF is a bad idea, I really meant that having both a one armed IF and a two armed IF called the same thing is a bad idea. I can live without one armed IF because it is trivial to write the obvious WHEN macro or use COND. Kent points out that the same problem occurs with named LET. Thus I withdraw my support for named LET syntax, and request ITERATE (maybe called LOOP) become an essential form. I would like to echo KMP's statements about regularization of naming in T. It is great! Even with a small programming language like Scheme, it should be the case that one needs to memorize a small set of naming rules, from which one can correctly guess the name of the particular function of interest. With T, I am noticely less dependent on having a manual by my side. I find it hard to believe that no standardization of macros is attempted by the Scheme Report, and hope that KMP's macro suggestion is given serious consideration. John  Received: from MIT-OZ by MIT-MC via Chaosnet; 27 MAR 85 15:38:18 EST Date: 27 Mar 1985 15:36 EST (Wed) Message-ID: From: Bill Rozas To: Chris Haynes Cc: scheme@MIT-MC.ARPA Subject: Draft of R.R. Report In-reply-to: Msg of 27 Mar 1985 13:22-EST from Chris Haynes I agree that #!NULL is unnecessary, but I don't agree that #!FALSE, etc should be quoted or that they should be bound to variables if used often. How come you don't ask for 0 to be quoted or bound to a variable if used often? RECURSE has the same bug that ITERATE has. The name implies a certain behaviour which the process it develops may not conform to. In particular, I usually use named let for loops, but I also use it for recursive procedures. The advantage of named let is that its name has no connotations, and that it reduces the number of "reserved" keywords. How am I supposed to define things if both DEFINE! and DEFINE go away? While I agree that CELLs are nice, why are they neccesary? They can easily be emulated with pairs, and any implementation which really uses them can support them optionally.  Received: from MIT-OZ by MIT-MC via Chaosnet; 27 MAR 85 15:30:35 EST Date: 27 Mar 1985 15:27 EST (Wed) Message-ID: From: Bill Rozas To: David Bartley Cc: Scheme@MIT-MC.ARPA Subject: ASSERT, ports, and NIL In-reply-to: Msg of 27 Mar 1985 11:38-EST from David Bartley I don't understand the problem. NULL? is essential, and as far as I know it is the only appropriate way to detect an empty list. How NULL? is implemented (in terms of EQ?, etc) is irrelevant. As far as I'm concerned there are multiple list terminators all of which are NULL?  Received: from MIT-OZ by MIT-MC via Chaosnet; 27 MAR 85 15:06:53 EST Date: Wed 27 Mar 85 15:05:41-EST From: Gerald Jay Sussman Subject: flames! To: scheme@MIT-MC Apparently everyone is hot-under-the-collar about one-armed IF! I cannot, for the life of me, see the harm of having one-armed IF. It is totally unambiguous, and it does not eat up a reserved word, what is more, it is useful. The alternative of WHEN is just feeping creaturism, as far as I can see. Just to add some fuel to the controversy, let me make a horrifying suggestion: (if p q) should be defined. It should return #!true if p is false, and it should return the value of q if p is true. This conflicts with no current usage since everyone agrees that one should not use the value of a side-effect operation. On the other hand, it gives IF the value of logical implication, in the boolean case -- an often useful feature. For example: (if (if p q) r s) -------  Received: from csnet-relay by MIT-MC.ARPA; 27 MAR 85 14:41:26 EST Received: from indiana by csnet-relay.csnet id ac23683; 27 Mar 85 14:32 EST Date: Wed, 27 Mar 85 13:22:21 est From: Chris Haynes Received: by iuvax.UUCP; id AA04491; Wed, 27 Mar 85 13:22:21 est To: scheme@mit-mc.ARPA Subject: Re: Draft of R.R. Report We are quite pleased with the report. Its synthesis is particularly remarkable given the considerable diversity of opinion expressed on the net. Asside from a few bugs and editorial suggestions communicated directly to Will (the most significant of which was that we agreed CALL-WITH-CURRENT-CONTINUATION was essential), our only remaining difficulties with the proposal are reflected in the following opinions. Given (1) the failure to specify under what circumstance an escape procedure invocation will result in closing a port opened with CALL-WITH-INPUT-FILE or CALL-WITH-OUTPUT-FILE, and (2) the optional nature of the explicit open and close operations, the result is that portable code can not be written for applications in which ports must be closed (often the case with todays operating systems) and escape procedures must be used (as in a coroutine application). The simplest solution is to make the open and close operations essential and the call-with operations optional. (In general, if an essential feature can not be fully specified, it should always be possible to program around the uncertainty.) If there is some reason why the open and close operations can not be essential (which shouuld be spelled out in a rationale statement), then the call-with functions should close files *only* when they return to the continuation of their invocation. It would then be possible to define the open and close operations using call-with functions and devious application of escape procedures. READ-CHAR-READY? is more descriptive than LISTEN?. The failure to specify what it returns at end of file is most unfortunate. We shouldn't waffle just because Common Lisp got it wrong. If you need to READ with assurance that you won't block, you have to write your own reader using READ-CHAR-READY? and READ-CHAR?. How about adding READ-READY?, which returns true iff an entire parsable object can be read without blocking. (A system that includes READ-ATOM should also include READ-ATOM-READY?.) An alternative to this predicate proliferation is to allow all read functions to return a distinguished object to indicate blocked input, as they may return a distinguished object to indicate end-of-file. Then all we need is a single predicate for this object, say HUNG?. The inclusion of the same numeric order predicates with and without a "?" looks terrible--as if we couldn't make up our mind, which is what standardization is all about. And both forms are essential! We were among of those who insisted on the simpler (no ?) forms because that is what we were accustomed to. But we'd rather switch than fight if the result is such conspicuous failure to standardize. At the very least, only one form should be essential. Would anyone else be willing to switch? #!TRUE, #!FALSE and #!NULL should not be constants. They are sufficiently ugly in programs that an extra quote mark in front is hardly worse. If they are required more than occasionally they should be bound to variables anyway. Why complicate the spartan syntax of Scheme with such things? #!NULL is unnecessary, for we already have a prefectly good way of printing and reading the empty list, (). #!NULL is also misnamed: traditionally NULL is a predicate and NIL is the empty list. RECURSE (introduced in Chez Scheme and added to Scheme 84) is much better than "named let" or ITERATE. If we can not agree, it would be better if neither RECURSE nor named let were mentioned in the report; but if the standard says some implementations permit named let, it should say others permit RECURSE. DEFINE! and DEFREC! should go away. (Yes, We were among those who wanted them originally, but they aren't worth it.) We're not fond of DEFINE either and wish it could go the same way, or at least be optional. NUMBER? is redundant, since all all numbers are complex in the proposal. If NUMBER? is included to allow for future generalization (quaternions?) or other reasons, then the rationale should be stated. WHEN is better than no one arm conditional. Having no one arm conditional is better than having IF's alternative optional. UNLESS we are better off without. The interaction of OBJECT-HASH and OBJECT-UNHASH with garbage collection should be specified. If we can't decide, the whole section should be deleted. A unary constructor is so fundamental that it is worth standardizing. It might be called BOX, REF or CELL. BOX probably would create the least confusion. Of course, we would also need associated selection, mutation and predicate functions. Chris Dan  Received: from yale by MIT-MC.ARPA; 27 MAR 85 14:40:41 EST Received: by YALE-BULLDOG.YALE.ARPA; 27 Mar 85 14:30:17 EST (Wed) Message-Id: <8503271930.AA25200@YALE-BULLDOG.YALE.ARPA> Received: from YALE-RING by YALE-RES via CHAOS; Wed, 27 Mar 85 14:28:58 EST Subject: Re: DRAFT of the Revised Revised Report Date: Wed, 27 Mar 85 14:29:07 EST From: Norman Adams To: Kent Dybvig Cc: scheme@MIT-MC In-Reply-To: Kent Dybvig , Wed, 27 Mar 85 01:25:03 est I also prefer the name "recurse" to "iterate" or named "let", and would like to see it required by the standard. As far as I can tell, "recurse" is not (yet!) an English word. I think "recur" would be better, though even that doesn't seem great. -------  Received: from csnet-relay by MIT-MC.ARPA; 27 MAR 85 14:24:01 EST Received: from ti-csl by csnet-relay.csnet id ac23602; 27 Mar 85 14:14 EST Date: 27 Mar 1985 1038-CST From: David Bartley Subject: Re: ASSERT, ports, and NIL To: Bartley%ti-csl.csnet@csnet-relay.arpa, Scheme@mit-mc.ARPA cc: Bartley%ti-csl.csnet@csnet-relay.arpa In-Reply-To: Your message of 22-Mar-85 1032-CST Received: from csl60 by ti-csl; Wed, 27 Mar 85 12:16 CST I'd like to clarify my dilemma concerning NIL. Jonathon Rees has shown, correctly, that one has no problem meeting both the Common Lisp and standard Scheme specifications for the empty list when the Scheme developer is able to affect the implementation of both languages. In that case, the underlying representation for the empty list is arbitrary (call it #!NULL). The Scheme reader treats the token NIL as a symbol. The Common Lisp reader interns the token NIL as #!NULL, and such critical routines as SYMBOLP and INTERN are modified to pretend that the value named #!NULL is a symbol. The situation I am concerned about is one in which Scheme is being implemented on top of an existing Lisp system which we are unable to modify. This is clearly a short term problem, but quite real while it lasts, and surely not unique to us. For example, it is a problem with Scheme implementations in Franz Lisp, and in ZetaLisp on Lisp Machines with ()==NIL hardwired into microcode. Let me walk through an instance of the problem. Assume an existing Lisp in which ()==NIL. In Lisp, generate a list; the list will be "terminated" with the symbol NIL in the last CDR. Pass the list to a Scheme routine, which cdr's down it until it reaches -- what? Unless #!NULL==NIL, Scheme can't detect the end of the list with a simple EQ? test. Sharing of list data between the two languages is effectively ruled out. Again: I'm not arguing that allowing #!NULL==NIL is "good," because it certainly isn't. I'm just asking for help in overcoming it and, barring a true solution, insertion of appropriate language in the revised revised Report acknowledging that it may be a necessity in some implementations. Alternatively, we could consider such an implementation a "near Scheme" and take the long term view that it is an interim step towards a "true Scheme." My purpose here is to raise the issue and possibly find a solution I hadn't thought of, not to quibble over legalities. Regards, David Bartley -------  Date: 27 March 1985 11:08-EST From: George J. Carrette Subject: plea for macros To: SCHEME @ MIT-MC How about biting the bullet and admitting macros in a way such that the language can have only one special form: LAMBDA, and all other forms are defined as macros, even "IF". (To repeat something I've either seen in a scheme paper or a scheme compiler: (IF A B C) => (*IF A (LAMBDA () B) (LAMBDA () C)), where *IF is a hand-coded SUBR). The proper packaging of macros and macro namespaces would put a lot of budding languages designers amoung us out of business. Think of scheme as an OEM oriented language. The designers can supply not much more than the primitives used a "Structure and Interpretation" the essential ideas and tools, then all manner of interesting (or not) things can be built on these without being the responsibility of the designer. The fact is that there are very large, useful, and even intrinsically interesting lisp programs that are built on very few primitives. Case in point: Macsyma. In order to do Macsyma in what common-lisp provides I had to write my own versions of: GET, EQUAL, ASSOC, PUTPROP, +, -, /, ^, \, MEMBER, MAP, MAPLIST. One page of code, big deal.  Received: from MIT-OZ by MIT-MC via Chaosnet; 27 MAR 85 10:24:48 EST Date: 27 Mar 1985 10:23 EST (Wed) Message-ID: From: Bill Rozas Subject: LIST? -- LIST To: linus!ramsdell@MITRE-BEDFORD.ARPA, SCHEME@MIT-MC LIST? will return true of those objects generated by LIST. That is precisely the case. The previous proposed definition for LIST? would return true of things which are not propoer lists, but LIST can only generate proper lists. Since there is no way (that I know of) of knowing whether a paricular pair-tree was created by a call to LIST or not (besides the fact that it seems undesirable), the only way to make this consistent is for LIST? to return #!true only on proper lists.  Received: from MIT-OZ by MIT-MC via Chaosnet; 27 MAR 85 10:20:54 EST Date: 27 Mar 1985 10:19 EST (Wed) Message-ID: From: Bill Rozas To: Kent M Pitman Cc: Scheme@MIT-MC.ARPA Subject: How to let macros work without defining what a macro is... In-reply-to: Msg of 26 Mar 1985 19:00-EST from Kent M Pitman This assumes that all macros work at the s-expression level, which is not true in our implementation. The only way to provide both (especially syntax-expand) in all cases would be to fully reduce to s-code and then invert to s-expressions. This would imply full (recursive) expansion always. In our system macros translate from s-expressions to s-code. There are a few utilities, however, to emulate s-expression level macros built on top of this. But macros like COND, LET, etc are never translated at the s-expression level.  Received: from MIT-OZ by MIT-MC via Chaosnet; 27 MAR 85 10:09:51 EST Date: 27 Mar 1985 10:08 EST (Wed) Message-ID: From: Bill Rozas To: Kent Dybvig Cc: scheme@MIT-MC.ARPA Subject: DRAFT of the Revised Revised Report In-reply-to: Msg of 27 Mar 1985 01:25-EST from Kent Dybvig (define (number-of-special-forms-in-standard date) (expt 2 (date->number date))) Now seriously, Named let corresponds to named-lambda in the same way that let corresponds to lambda. If argument list destructuring is not a problem for let, it is not a problem for named let. If it is, the only "destructuring" allowed in lambda is for &rest arguments (dot notation). The syntax for LET can be extended to accept rest arguments: Note: Each of the subforms of the binding list of a let must be a list whose CAR is the identifier and whose CADR is the expression whose value the identifier will receive. Thus the syntax can be extended to the following: (let (( ) . . . ( ) ; Note: no parens . . . ) ) The rest identifier can be recognized by not being in a list, and all forms following it are the expressions whose values will be collected into the &rest list. I'm not advocating for this extension in the standard, but any implementation which wants named-let can easily extend the syntax for let and named-let so that this "destructuring" is provided. You may be right in that this means that there are 2 special forms with the same keyword, but that's alright with me. I don't want to have to remember 50 thousand different identifiers which are "special" to ths system, and I cannot use as identifiers in portable code since I don't know what happens in all implementations when a lambda-parameter attempts to shadow them. I have no problems recognizing one-armed ifs. They only appear in the middle of sequences (sorry, begins) and indentation allows me to see whether there is a second arm or not.  Received: from MIT-OZ by MIT-MC via Chaosnet; 27 MAR 85 09:38:40 EST Date: 27 Mar 1985 09:37 EST (Wed) Message-ID: From: Bill Rozas To: linus!ramsdell@MITRE-BEDFORD.ARPA, scheme@MIT-MC.ARPA Subject: function names. In-reply-to: Msg of 26 Mar 1985 17:36-EST Tue 26 Mar 85 10:00:27 est from linus!ramsdell at Mitre-Bedford, linus!ramsdell (John D. Ramsdell) at Mitre-Bedford What does WALK stand for? Something like FOR-EACH would be better (except that it suggests the opposite order for the arguments). WALK suggests something that performs a tree walk to me.  Received: from csnet-relay by MIT-MC.ARPA; 27 MAR 85 05:12:36 EST Received: from unc by csnet-relay.csnet id ac21376; 27 Mar 85 5:04 EST Received: by unc (4.12/4.7) id AA21906; Wed, 27 Mar 85 01:25:03 est Date: Wed, 27 Mar 85 01:25:03 est From: Kent Dybvig Message-Id: <8503270625.AA21906@unc> To: scheme@mit-mc.ARPA Subject: Re: DRAFT of the Revised Revised Report 2 & 5) The reason for not having a different name for the one-armed IF is that we tried very hard to keep the number of special forms to a minimum. Since they are easily distinguished by the number of subforms, there is no need to add another. Note that the same philosophy leads MIT to have named LET, which is exactly (except for the name) what ITERATE used to be. I used to have both one-armed "if" and named "let" in my system. I got tired of explaining that the return value of "if" is defined if there are two arms but undefined if there is one arm. With "let", I got tired of explaining that it creates simple bindings in the one case and performs looping/recursion in the other. "let" is such an important special form that it is inappropriate to give it two such different meanings. If you have two syntaxes for one special form, then you have two special forms. The only difference is that they are distinguished by more than just the syntax keyword. If one of the two is a natural extension of the other, fine, otherwise the situation leads to unwarranted confusion. (The "number of subforms" is an easy distinction for a compiler, but not always for the eye. I prefer the distinction to be more obvious.) Incidentally, I had named "let" in my system to correspond to named "lambda", so named "let" was easy to explain in terms of named "lambda". Of course, the destructuring lambda-list syntax blew that away. Conclusions: I prefer to include "when" and "unless". Cond is not an acceptable alternative; it is best used to reduce nesting in long sequences of "if-then-else-if" expressions. While we're at it, I'd like to see both "cond" and "case" require an "else" clause. I also prefer the name "recurse" to "iterate" or named "let", and would like to see it required by the standard. ..Kent  Received: from MIT-OZ by MIT-MC via Chaosnet; 26 MAR 85 21:39:06 EST Date: Tue 26 Mar 85 21:37:53-EST From: Gerald Jay Sussman Subject: car/cdr To: hudak@YALE.ARPA cc: scheme@MIT-MC I dunno if I ever responded to you about this, but I really like CAR, CDR, CADADR and all the rest. The special advantage is that they can be pronounced, so I can say them fast over the phone to someone and they can be expected to understand what I said. Imagine having to say "First of Rest of First of Rest" when one can say "CADADR"... The key is that combinations of "A" and "D" are not tongue-twisters. I think that this is the only name convention I really am attached to, and it is NOT for historical (hysterical?) reasons, but pragmatic ones. -------  Received: from csnet-relay by MIT-MC.ARPA; 26 MAR 85 20:56:28 EST Received: from ti-csl by csnet-relay.csnet id ab19022; 26 Mar 85 20:19 EST Date: 26 Mar 1985 1637-CST From: David Bartley Subject: Missing CSNET messages To: Scheme@mit-mc.ARPA cc: Bartley%ti-csl.csnet@csnet-relay.arpa, Scheme.users%ti-csl.csnet@csnet-relay.arpa Received: from csl60 by ti-csl; Tue, 26 Mar 85 19:16 CST A failure at our end caused TI-CSL to miss messages directed to it yesterday. Would anyone who sent a message to me (Bartley@TI-CSL) or indirectly to us through Scheme@MIT-MC please resend their messages? Senders include: MW@Brandeis JAR@MIT-MC linus!ramsdell... the CSNET postmaster GJC@MIT-MC HUDAK%YALE@MIT-MC.ARPA I don't know how to interpret some of the addresses that failed. We apparently received messages for "Scheme@Users" which were rejected. Our local Scheme group mailing list is Scheme.Users@TI-CSL, if that's what you're looking for. Regards, David Bartley -------  Received: from SCRC-STONY-BROOK by MIT-MC via Chaosnet; 26 MAR 85 20:00:04 EST Received: from SCRC-RIO-DE-JANEIRO by SCRC-STONY-BROOK via CHAOS with CHAOS-MAIL id 203911; Tue 26-Mar-85 18:59:50-EST Date: Tue, 26 Mar 85 19:00 EST From: Kent M Pitman Subject: How to let macros work without defining what a macro is... To: Scheme@MIT-MC.ARPA cc: KMP@SCRC-STONY-BROOK.ARPA Message-ID: <850326190013.5.KMP@RIO-DE-JANEIRO.SCRC.Symbolics.COM> Even though it was not possible to agree on how macros should be specified in Scheme, I believe it important that they be provided in some form in all implementations and have a proposal for a semi-invisible way to do this. (PRIMITIVE-SYNTAX-EXPAND form) required Takes any form and returns a form which is guaranteed not to have any syntax other than that required by the language. This function signals an error if it receives a form which is a user-defined macro. (Hence, this function is for writing code which is defined only over portable code). The fact that it only allows "primitive" macros to be expanded is what makes it not need an ENV argument, since the primitive language does not allow one to change the set of special forms. Actually, there are two variations of this proposal which are probably consistent. One says that it expands all levels; the other says that it expands just one level. I think it really doesn't matter. For consistency with SYNTAX-EXPAND below, let's assume it doesn't recurse into the form. The intent is (though this is not part of the proposal) that languages providing a macro facility would have a function: (SYNTAX-EXPAND form env) optional Takes any form and a syntax environment and returns a form which has no other syntax than lisp syntax. This form will expand macros and call PRIMITIVE-SYNTAX-EXPAND if the underlying representation makes that necessary. The result should be a form which contains only primitive forms. This would allow us to define system extensions which we could -describe- as macro translations without telling users how those things were defined. Eg, they might be macros or they might be special forms. Consider that (function/macro names in this example being hypothetical, but you'll get the idea I hope): (WITH-OPEN-FILE (STREAM-VAR FILE-NAME . OPTIONS) . BODY) might be defined to the user to "be the same as": (CALL-WITH-OPEN-STREAM (LAMBDA (STREAM-VAR) BODY) FILE-NAME . OPTIONS) and (PRIMITIVE-SYNTAX-EXPAND '(WITH-OPEN-FILE (OUTSTREAM "MY.FILE" 'OUT) (PRINT "Hi there." OUTSTREAM))) would be defined to return (CALL-SYNTAX-EXPAND (LAMBDA (OUTSTREAM) (PRINT "Hi there." OUTSTREAM)) "MY.FILE" 'OUT) regardless of whether the system actually did that expansion when it ENCLOSEd the form... This proposal basically gives the user the ability to maintain the illusion that there are a fixed set of special forms when he does code-manipulation even if we later decide that a few extra special forms would be a good idea. eg, WHEN, UNLESS, etc. would not burden code-walking writers since (PRIMITIVE-SYNTAX-EXPAND form) would know about those forms and how to magically make them into something meaningful like COND or IF (are they both primitive? they wouldn't have to be under this proposal). This also allows implementations to add new special forms which are not in standard Scheme but which are still handleable by standard Scheme. Note well that programs which expect to work on code which might call user-defined macros would HAVE TO use SYNTAX-EXPAND and could not use PRIMITIVE-SYNTAX-EXPAND even if PRIMITIVE-SYNTAX-EXPAND would appear to work, since PRIMITIVE-SYNTAX-EXPAND should NOT be sensitive to any redefinitions of system primitive special forms that extended Scheme implementations might allow. That is, if a dialect allowed (DEFINE-SYNTAX (IF ...) ...variant-definition...) then (SYNTAX-EXPAND '(IF ...)) should get the variant expansion but (PRIMITIVE-SYNTAX-EXPAND '(IF ...)) should get the "standard expansion". This is completely necessary in order for any of this proposal to make sense.  Received: from SCRC-STONY-BROOK by MIT-MC via Chaosnet; 26 MAR 85 19:59:59 EST Received: from SCRC-RIO-DE-JANEIRO by SCRC-STONY-BROOK via CHAOS with CHAOS-MAIL id 203900; Tue 26-Mar-85 18:28:31-EST Date: Tue, 26 Mar 85 18:28 EST From: Kent M Pitman Subject: function names. To: linus!ramsdell@MITRE-BEDFORD.ARPA, scheme@MIT-MC.ARPA In-Reply-To: <8503261500.AA09753@linus.UUCP> Message-ID: <850326182851.4.KMP@RIO-DE-JANEIRO.SCRC.Symbolics.COM> Date: Tue, 26 Mar 85 10:00:27 est From: linus!ramsdell (John D. Ramsdell) Message-Id: <8503261500.AA09753@linus.UUCP> Subject: function names. It is clear no agreement can be reached about CAR and CDR. I withdraw the suggested name changes except for: MAPCAR => MAP and MAPC => WALK. Is there disagreement here? I support these name changes (perhaps not surprisingly since I'm partly responsible for them being that way in T). For those who are curious about the justification for the MAPCAR->MAP change: * MAP is nicely generic. You can't later extend MAPCAR to work on general structures like matrices without having to apologize for the fact that "successive cars of a matrix" is not as meaningful as it is for lists. * Whenever anyone complained to me that MAP had another meaning and that people would be confused, I would take them by the arm and drag them into the office of an unsuspecting person to whom I would say: "Please write an expression on the board which maps a function F across a list L." The person would -always- write (MAPCAR F L) No one -ever- wrote (MAP F L) The reason is that MAP is so uncommon that no one even bothers to verbally distinguish it from MAPCAR. You have to say "Please write an expression which calls the function MAP on arguments which are the function F and the list L." to get the other behavior. From this I conclude that it is "natural" for the MAPCAR operation to be called MAP. The arguments for MAPC->WALK are these: MAPCAR returns a value which is intended to be used. MAPC returns a value which is not intended to be used. MAPCAR takes a function which is not intended to have a side-effect. MAPC takes a function which is intended to have a side-effect. These two functions have little more in common than the fact that their first arg is a function and the second is a list. Also, the "C" is pretty random (presumably deriving from the "CAR" in MAPCAR). As with MAPCAR, if MAPC were ever extended to map across (notice I'm using the word "map" and you -know- what I mean) matrices, arrays, vectors, etc. then again the "C" would be still harder to explain. So we wanted something that connoted `visiting elements' but not a priori constraining the way those elements were enumerated and not connoting any idea of return value. WALK was chosen because it's short, sounds like what it does ("walks around in structures"), etc. By the way, T uses MAPCDR and WALKCDR to mean the "less common" variants (MAPLIST and MAP, respectively, so you don't have to look them up). The fact that the names are longer is appropriate (common things should get the short names), as is the fact that the term CDR is included (since these don't make sense to generalize to non-CDRable things (although if you generalized CDR, they would make sense and their name would still be fine)). To help with this convention, T renames Lisp's traditional LAST function to LASTCDR, so what is sometimes called LASTCAR can be called LAST (for naming consistency with MAP/MAPCDR, WALK/WALKCDR, NTH/NTHCDR). It is this sort of regularization of naming which has led users of T to spontaneously send fan mail saying how much they like the clarity of expression the new names encourage. (Most other languages I've seen get mostly gripes about naming and little or no praise.) What gripes we get are usually nitpicky things where users (having decided things are really winning) want to encourage us to go the rest of the way toward making the naming consistent in the places where we hesitated to make choices which would clearly have made the language even more regular. Almost no one has complained of missing an old name (especially since for the cases where they care, it's so easy to define synonyms). -kmp  Received: from mitre-bedford by MIT-MC.ARPA; 26 MAR 85 17:39:48 EST Date: 26 Mar 1985 17:36:09-EST From: linus!ramsdell@Mitre-Bedford Received: by linus.UUCP (4.12/4.7) id AA09753; Tue, 26 Mar 85 10:00:27 est Date: Tue, 26 Mar 85 10:00:27 est From: linus!ramsdell (John D. Ramsdell) Message-Id: <8503261500.AA09753@linus.UUCP> To: bccvax!scheme@mit-mc.arpa Subject: function names. It is clear no agreement can be reached about CAR and CDR. I withdraw the suggested name changes except for: MAPCAR => MAP and MAPC => WALK. Is there disagreement here? John  Received: from mitre-bedford by MIT-MC.ARPA; 26 MAR 85 17:38:57 EST Date: 26 Mar 1985 17:36:04-EST From: linus!ramsdell@Mitre-Bedford Received: by linus.UUCP (4.12/4.7) id AA09666; Tue, 26 Mar 85 09:54:52 est Date: Tue, 26 Mar 85 09:54:52 est From: linus!ramsdell (John D. Ramsdell) Message-Id: <8503261454.AA09666@linus.UUCP> To: bccvax!scheme@mit-mc.arpa Subject: LIST? Cc: ramsdell I thought LIST? would be a good name for the predicate that returns true to the objects generated by LIST, just an VECTOR? returns true to the objects generated by VECTOR and PAIR? returns true to the objects generated by PAIR ... eh ... CONS. Opps! Nevermind. John PS Maybe STRING should take a variable number of characters and returns a string. (define (string . chars) (list->string chars))  Received: from mitre-bedford by MIT-MC.ARPA; 26 MAR 85 17:38:09 EST Date: 26 Mar 1985 17:36:00-EST From: linus!ramsdell@Mitre-Bedford Received: by linus.UUCP (4.12/4.7) id AA09491; Tue, 26 Mar 85 09:43:17 est Date: Tue, 26 Mar 85 09:43:17 est From: linus!ramsdell (John D. Ramsdell) Message-Id: <8503261443.AA09491@linus.UUCP> To: bccvax!scheme@mit-mc.arpa Subject: WHEN OK, so WHEN is a bad idea. However, I agree with Jonathan that one armed IF is a bad idea. John  Date: 25 March 1985 21:43-EST From: George J. Carrette Subject: NIL, experience with VAX-NIL, or NIL is nothing to worry about. To: Bartley%ti-csl.csnet @ CSNET-RELAY cc: JAR @ MIT-MC, SCHEME @ MIT-MC In-reply-to: Msg of 25 Mar 1985 1153-CST from David Bartley I was able to put a rational, i.e. (NOT (EQ 'NIL '())), scheme on top of both VAX-NIL and LMI Release 2.0, both of which are common-lisp. In fact, in the inner guts of VAX-NIL we have that () is not a symbol, and the first distributed versions of NIL (SYMBOLP ()) => (), but peer presure forced us (at that time Glenn Burke and myself) to toe-the-line and change, if not the guts, but what the user sees in the way of a type scheme. One argument TYPEP is not supported in common-lisp, so the following output from VAX-NIL should not be suprising: (TYPEP ()) => NULL (TYPEP 'FOO) => SI:NON-NULL-SYMBOL (TYPEP '(FOO)) => SI:SIMPLE-CONS Of course (SYMBOLP X) <==> (TYPEP X 'SYMBOL) <==> (TYPEP X '(OR NULL SI:NON-NULL-SYMBOL)). LISTP is then (TYPEP X '(OR NULL SI:SIMPLE-CONS SI:EXTENDED-CONS)). The SI:EXTENDED-CONS is something GSB put in for BRANDX, (remember hunks?) but lets not get into that. There are of course special type masks cleverly arranged to be used with the VAX instructions for doing these type unions, so that the most useful cases of two-argument TYPEP are in-line-coded. The inner workings of the microcoded lisps such as on the LMI LAMBDA and TI EXPLORER play similar hacks with NIL. Its just one of those things. Unfortunately the Common-Lisp committee, of which I felt like an out-gunned member, had the "guts" to change MEMBER, ASSOC, EQUAL, *, /, +,^, but not to address this NIL thing. If you reread JAR's analysis, and I hope I have provided some hints into the inner workings of some of the base lisps in question, then you should see what he is talking about.  Received: from yale by MIT-MC.ARPA; 25 MAR 85 20:58:07 EST Received: by YALE-BULLDOG.YALE.ARPA; 24 Mar 85 19:54:45 EST (Sun) Message-Id: <8503250054.AA23410@YALE-BULLDOG.YALE.ARPA> Received: from YALE-RING by YALE-RES via CHAOS; Sun, 24 Mar 85 20:00:23 EST Subject: CAR and CDR again Date: Sun, 24 Mar 85 20:00:25 EST From: Paul Hudak To: CPH%MIT-OZ@MIT-MC Cc: Scheme@MIT-MC, Jinx%MIT-OZ@MIT-MC In-Reply-To: CPH%MIT-OZ@MIT-MC.ARPA, Sun, 24 Mar 1985 02:26 EST Maybe I'm just being old and tired, but I like CAR and CDR! I would be saddened to see them lost in a wave of "modernization". Would you consider changing LAMBDA to something else? I think that CAR, CDR and CONS have the same historical value (as does COND, I suppose). LAMBDA derives from the Lambda Calculus, where the association with lambda expressions is clear. CAR and CDR derive from obscure machine features whose meaning is not clear. CONS is not so bad because it is short for CONSTRUCT, although PAIR would probably be better. Obviously, I like keeping in touch with the past, as long as it doesn't cloud the thinking of the future. This doesn't seem to be such a case; might as well call the operations "0" and "1", if you want to be really, really efficient. Efficiency, of course, is not the rationale for any of this. It's choosing a name that makes sense. Why did we choose names like WALK instead of the "traditional" MAPC or MAPCAR or whatever? Some people probably feel as bad about loosing those as they would for CAR and CDR. However, I'm not one to stand in the way of preserving history! I was curious to see how people felt though, and wanted to see how far Essential Scheme's modernization effort, which does exist, was willing to go. -Paul  Received: from mitre-bedford by MIT-MC.ARPA; 25 MAR 85 17:43:15 EST Date: 25 Mar 1985 17:36:35-EST From: linus!ramsdell@Mitre-Bedford Received: by linus.UUCP (4.12/4.7) id AA25113; Mon, 25 Mar 85 08:01:59 est Date: Mon, 25 Mar 85 08:01:59 est From: linus!ramsdell (John D. Ramsdell) Message-Id: <8503251301.AA25113@linus.UUCP> To: bccvax!scheme@mit-mc.arpa Subject: ITERATE If named LET is to replace ITERATE, that is fine with me. Please make it an essential form. John  Received: from csnet-relay by MIT-MC.ARPA; 25 MAR 85 15:44:44 EST Received: from ti-csl by csnet-relay.csnet id ae10480; 25 Mar 85 15:34 EST Date: 25 Mar 1985 1153-CST From: David Bartley Subject: Re: NIL To: JAR@mit-mc.ARPA, Bartley%ti-csl.csnet@csnet-relay.arpa cc: SCHEME@mit-mc.ARPA, Bartley%ti-csl.csnet@csnet-relay.arpa In-Reply-To: Your message of 22-Mar-85 1552-CST Received: from csl60 by ti-csl; Mon, 25 Mar 85 13:41 CST Re: Jonathon's suggestion for rationalizing NIL in Common Lisp and () in Scheme... When Common Lisp is implemented on top of Scheme, or at the same time as Scheme, your second approach is quite workable. It relies on making the representation of the empty list suit Scheme's needs and patching up a few components of Common Lisp so () "looks" like the symbol NIL. My only concern here is that I'd like to use the same reader, the same SYMBOL?/ SYMBOLP routine, etc., but I'm willing to give in on those. The first example you gave, bringing up Scheme (T) on top of Common Lisp (Maclisp), still has a problem. If Scheme is introduced into a system in which the idea that NIL=='NIL=='() is deeply ingrained (e.g. in microcode), then I see no way to allow a user to jump back and forth between processes written in the two languages and which must share the same list data, without making 'NIL=='(). I intend to make new systems conform along the lines you suggest. However, I still would like a multi-lingual implementation built on an existing Lisp system to be allowed by the standard. Regards, David Bartley -------  Received: from MIT-OZ by MIT-MC via Chaosnet; 24 MAR 85 03:06:38 EST Date: Sun, 24 Mar 1985 02:37 EST Message-ID: From: CPH%MIT-OZ@MIT-MC.ARPA To: David Bartley Cc: Scheme@MIT-MC.ARPA Subject: Revisions to String Proposal In-reply-to: Msg of 22 Mar 1985 12:05-EST from David Bartley Date: Friday, 22 March 1985 12:05-EST From: David Bartley A minor suggestion -- perhaps the = and < comparators could be made essential and the <=, >, and >= comparators made optional. I think that this is quite reasonable -- often I try to code this way. Although... is the cost of tying up these few identifiers worth the extra expressive power they may bring? I think it probably is; sometimes it seems that one wants to say "(>= ...)" rather than "(not (< ...))". The extra symbols will be bound in people's minds whether or not they are in the "revised revised report" or not.  Received: from MIT-OZ by MIT-MC via Chaosnet; 24 MAR 85 03:06:34 EST Date: Sun, 24 Mar 1985 02:26 EST Message-ID: From: CPH%MIT-OZ@MIT-MC.ARPA To: Hudak@YALE.ARPA, Jinx%MIT-OZ@MIT-MC.ARPA Cc: Scheme@MIT-MC In-reply-to: Msg of 21 Mar 1985 21:28-EST from Paul Hudak Maybe I'm just being old and tired, but I like CAR and CDR! I would be saddened to see them lost in a wave of "modernization". Would you consider changing LAMBDA to something else? I think that CAR, CDR and CONS have the same historical value (as does COND, I suppose). Obviously, I like keeping in touch with the past, as long as it doesn't cloud the thinking of the future. This doesn't seem to be such a case; might as well call the operations "0" and "1", if you want to be really, really efficient.  Received: from SU-SCORE.ARPA by MIT-MC.ARPA; 22 MAR 85 23:49:16 EST Date: Fri 22 Mar 85 20:47:41-PST From: Andy Freeman Subject: Re: DRAFT of the Revised Revised Report To: Hudak@YALE.ARPA cc: JINX%MIT-OZ@MIT-MC.ARPA, scheme@MIT-MC.ARPA In-Reply-To: Message from "Paul Hudak " of Fri 22 Mar 85 18:36:19-PST Actually I prefer car/cdr over head/tail and first/rest because the former have no outside associations (I've never seen a 7094) and the latter do. I then define more appropriate names that reflect the meaning in the program. More meaningful (or less ugly) names don't encourage this. (I'm not sure about -<>-, etc., but they share the unnecessary topological connotations of head/tail and first/rest.) Then again, I don't want my cons operations to suggest that I'm working on lists, just cons'. (Someone has already mentioned the difference between cons? and list?) Also, car/cdr are nice harmless bit of history. -andy -------  Received: from yale by MIT-MC.ARPA; 22 MAR 85 21:21:48 EST Received: by YALE-BULLDOG.YALE.ARPA; 21 Mar 85 21:31:50 EST (Thu) Message-Id: <8503220231.AA28348@YALE-BULLDOG.YALE.ARPA> Received: from YALE-RING by YALE-RES via CHAOS; Thu, 21 Mar 85 21:28:34 EST Subject: Re: DRAFT of the Revised Revised Report Date: Thu, 21 Mar 85 21:28:35 EST From: Paul Hudak To: Bill Rozas Cc: scheme@MIT-MC In-Reply-To: Bill Rozas , 21 Mar 1985 18:49 EST (Thu) 0) I agree that car, cdr, etc, are poor names, yet I have not seen good alternatives to them except first and rest, which don't nest (a la c....r) too well. I think that their use is so entrenched that it would be hard to get used to other things. Another possibility is head and tail. Both of them actually *do* nest fairly well; for example, according to the following scheme: car&cdr head&tail first&rest ---------------------------------- caar hhd fft cadr htl frt cdar thd rft cddr ttl rrt caaar hhhd ffft caadr hhtl ffrt cadar hthd frft caddr httl frrt ... ... ... My personal preference is head and tail, but anything is better than car and cdr. -Paul  Date: 22 March 1985 16:52-EST From: Jonathan A Rees Subject: NIL To: Bartley%ti-csl.csnet @ CSNET-RELAY cc: SCHEME @ MIT-MC In-reply-to: Msg of 22 Mar 1985 1032-CST from David Bartley Date: 22 Mar 1985 1032-CST From: David Bartley 3. I hate NIL. However, I have a problem of great practical significance to our work that I'd like help with. As much as I abhor it personally, Common Lisp equates the empty list, the logical value, and the symbol NIL. Here at TI's Computer Science Lab, we are building experimental multi-lingual program development environments for various machines (including our Explorer Lisp machine) in which Scheme, Common Lisp, Prolog, and other languages need to co-exist. I see no practical way to avoid defining our Scheme's #!NULL and #!FALSE as anything other than the symbol NIL. Otherwise the two languages cannot share list data. If anyone has a good general solution to this problem, I'd like to hear of it. If not, I ask that this interpretation be permitted by the revised report. Surely we aren't the only ones interested in multi-lingual issues? I don't think we need to or should allow either false or the empty list to be a symbol. In our PDP-10 Maclisp prototype of T, we solved the inconsistency by doing (REMOB 'NIL). Maclisp had a symbol NIL, which was the same as false and the empty list, and T had a symbol NIL (created, after the REMOB happened, the first time that READ encountered the sequence "NIL"), which was different from the Maclisp symbol NIL. T and Maclisp coexisted happily. Then we did: (defun symbol? (x) (and x (symbolp x))) Similarly, in the Common Lisp emulation package in T, we created a parameter to READ which caused the atom NIL to read in as the empty list, using the same framework that allows atoms like 1E3 and 5/8 to be read as numbers instead of symbols. Then, we emulated Common Lisp symbols as the union of T symbols and the empty list: (define (symbolp x) (or (symbol? x) (null? x))) (define (intern x) (if (string-equal? x "NIL") ;(no packages) '() (string->symbol x))) (define (symbol-name x) (xcond ((symbol? x) (symbol->string x)) ((null? x) "NIL"))) and so on. So Common Lisp (with its own environment, syntax table, and read table distinct from T's) coexisted happily with T. The much harder problem is emulating the status quo when false and the empty list are distinguished, but let's not talk about that. Jonathan  Received: from MIT-OZ by MIT-MC via Chaosnet; 22 MAR 85 16:44:32 EST Date: Fri 22 Mar 85 16:43:08-EST From: Gerald Jay Sussman Subject: Re: ASSERT, ports, and NIL To: Bartley%ti-csl.csnet@CSNET-RELAY.ARPA cc: scheme@MIT-MC In-Reply-To: Message from "David Bartley " of Fri 22 Mar 85 11:32:00-EST I like "port" too! -------  Received: from csnet-relay by MIT-MC.ARPA; 22 MAR 85 16:22:54 EST Received: from ti-csl by csnet-relay.csnet id ag00183; 22 Mar 85 14:13 EST Date: 22 Mar 1985 1105-CST From: David Bartley Subject: Re: Revisions to String Proposal To: CPH%MIT-OZ@mit-mc.ARPA, Scheme@mit-mc.ARPA cc: Bartley%ti-csl.csnet@csnet-relay.arpa In-Reply-To: Your message of 20-Mar-85 0242-CST Received: from csl60 by ti-csl; Fri, 22 Mar 85 12:26 CST I am quite pleased with Chris' revised string proposal. A minor suggestion -- perhaps the = and < comparators could be made essential and the <=, >, and >= comparators made optional. Regards, David Bartley -------  Received: from csnet-relay by MIT-MC.ARPA; 22 MAR 85 16:22:23 EST Received: from ti-csl by csnet-relay.csnet id ac00183; 22 Mar 85 14:11 EST Date: 22 Mar 1985 1032-CST From: David Bartley Subject: ASSERT, ports, and NIL To: Scheme@mit-mc.ARPA cc: Bartley%ti-csl.csnet@csnet-relay.arpa Received: from csl60 by ti-csl; Fri, 22 Mar 85 10:56 CST I have some miscellaneous comments on topics raised recently... 1. I like Will's ASSERT. I have used the same approach before in compilers for Pascal and other languages and find that it is quite appropriate for both the programmer and a dataflow-oriented optimizer. 2. I like PORT. My Webster's includes the following definitions: (2a) an opening for intake or exhaust of a fluid ... (2c) a place of access to a system This is close enough to the concept we have in mind. Frankly, "raft" seems too whimsical for a language that deserves to be taken seriously. 3. I hate NIL. However, I have a problem of great practical significance to our work that I'd like help with. As much as I abhor it personally, Common Lisp equates the empty list, the logical value, and the symbol NIL. Here at TI's Computer Science Lab, we are building experimental multi-lingual program development environments for various machines (including our Explorer Lisp machine) in which Scheme, Common Lisp, Prolog, and other languages need to co-exist. I see no practical way to avoid defining our Scheme's #!NULL and #!FALSE as anything other than the symbol NIL. Otherwise the two languages cannot share list data. If anyone has a good general solution to this problem, I'd like to hear of it. If not, I ask that this interpretation be permitted by the revised report. Surely we aren't the only ones interested in multi-lingual issues? Regards, David Bartley -------  Date: 22 March 1985 12:51-EST From: Jonathan A Rees Subject: (if a b) To: SCHEME @ MIT-MC In-reply-to: Msg of 21 Mar 1985 17:35:34-EST Thu 21 Mar 85 13:35:25 est from linus!ramsdell at Mitre-Bedford, linus!ramsdell (John D. Ramsdell) at Mitre-Bedford I'm in agreement with John Ramsdell in opposing amputated IF (although I also agree with Bill Rozas that WHEN is a bad idea). IF-forms should always have two arms. Could someone explain to me again why it is a good idea? It complicates the language. You can always use (COND (test ...)) if you really don't want to write another arm. And your dialect can optionally include the amputated variant. I don't see how one can simultaneously support (IF A B) and oppose (DEFINE (FOO X) Y). Jonathan  Received: from MIT-OZ by MIT-MC via Chaosnet; 21 MAR 85 18:51:21 EST Date: 21 Mar 1985 18:49 EST (Thu) Message-ID: From: Bill Rozas To: scheme@MIT-MC Subject: DRAFT of the Revised Revised Report In-reply-to: Msg of 21 Mar 1985 17:35-EST Thu 21 Mar 85 13:35:25 est from linus!ramsdell at Mitre-Bedford, linus!ramsdell (John D. Ramsdell) at Mitre-Bedford 0) I agree that car, cdr, etc, are poor names, yet I have not seen good alternatives to them except first and rest, which don't nest (a la c....r) too well. I think that their use is so entrenched that it would be hard to get used to other things. Random related comment: A possibility we have not considered is general-car-cdr: (general-car-cdr #b1 l) = l (general-car-cdr #b10 l) = (car l) (general-car-cdr #b11 l) = (cdr l) (general-car-cdr #b101 l) = (cadr l) (general-car-cdr #b110 l) = (cdar l) etc. Thus c....r = (lambda (l) (general-car-cdr n l)) Where n = #b1**** with the mapping a->0 b->1 C...r (except car and cdr) are defined like that in MIT Scheme. Note that general-car-cdr is not limited to a length of 4 for the chain of car-cdrs, and is trivial to implement. 2 & 5) The reason for not having a different name for the one-armed IF is that we tried very hard to keep the number of special forms to a minimum. Since they are easily distinguished by the number of subforms, there is no need to add another. Note that the same philosophy leads MIT to have named LET, which is exactly (except for the name) what ITERATE used to be. 3) I don't think that the syntax (named-lambda name expr) is a good idea as a substitute for rec. An extension of rec would allow something like (rec a-circular-list (cons 'a a-circular-list)) where there are no lambdas, so named-lambda is inappropriate. Note that both rec and named-lambda are optional, so there is no need to drop any. Different dialects have preferences for one over the other, so an arbitrary decision would probably not sit well. Note also that MIT Scheme allows also the syntax (named-lambda ((foo . args1) . args2) . body) = (named-lambda (foo . args1) (lambda args2 . body)) which is not on the report. 9) I don't think that the list? you propose is missing. I think list? should be defined as (define (list? l) (or (null? l) (and (pair? l) (list? (cdr l))))) , in other words, proper-list?. What you define as list? is what we (MIT-Scheme) call weak-list? which we have never found a real use for. I would like to eliminate the confusion about lists. I propose that list refer to proper lists, and anything else built out of pairs be called a pair-graph. Thus lists are pair-graphs but not viceversa. The procedure LIST produces a list, and the procedure LIST? tests whether an object is one.  Received: from mitre-bedford by MIT-MC.ARPA; 21 MAR 85 17:47:31 EST Date: 21 Mar 1985 17:35:34-EST From: linus!ramsdell@Mitre-Bedford Received: by linus.UUCP (4.12/4.7) id AA21592; Thu, 21 Mar 85 13:35:25 est Date: Thu, 21 Mar 85 13:35:25 est From: linus!ramsdell (John D. Ramsdell) Message-Id: <8503211835.AA21592@linus.UUCP> To: bccvax!scheme@mit-mc.arpa Subject: Re: DRAFT of the Revised Revised Report A few comments: 1) How about consolidating descriptions of all procedures that convert things to a string. E.g. symbol->string should be described in the same section as number->string. Part II: A catalog of Scheme II.5 Symbols II.6 Numbers II.7 Vectors II.8 Strings II.9 String conversions. <-! II.10 The object table II.11 Procedures II.12 Ports II.13 Input II.14 Output 2) When I use a one arm if, I am usually writting side effects, but with a two arm if, I am not. I suggest these two different uses be glorified with different names. In particular, let's name the one arm if "when". (if condition consequent alternative) essential special form (when condition expr1 expr2 ...) essential special form The result of the when is undefined. 3) Since define has two syntaxes, how about giving rec or named-lambda the same two. You could drop one of the special forms as a result. (named-lambda (name var1 ...) expr ...) special form (named-lambda name expr ...) special form 4) I think it is great that the value of set! is undefined. 5) What happened to iterate? I use it much more than I use do. It would be nice to name it loop, but I'm sure the T people would object. 6) Macro not defined in the begining of the report. v `pattern macro special form 7) It is excellent that there is an attempt to stop confusing the empty list with boolean false. 8) The phrase "more inclined to..." does not sound like the kind of words one should use in a description of eqv?. be used to compare numbers. The eqv? procedure is just like eq? except that it is more inclined to say that two numbers are the same. 9) Missing predicate: list?. (list? obj) essential procedure (define (list? l) (or (pair? l) (null? l))) 10) I was happy to find that property lists are not part of the definition of symbols. 11) I vote for the long name instead of call/cc. (call-with-current-continuation f) procedure John  Received: from mitre-bedford by MIT-MC.ARPA; 21 MAR 85 17:46:38 EST Date: 21 Mar 1985 17:35:15-EST From: linus!ramsdell@Mitre-Bedford Received: by linus.UUCP (4.12/4.7) id AA18613; Thu, 21 Mar 85 09:30:14 est Date: Thu, 21 Mar 85 09:30:14 est From: linus!ramsdell (John D. Ramsdell) Message-Id: <8503211430.AA18613@linus.UUCP> To: bccvax!scheme@mit-mc.arpa Subject: MAPC and MAPCAR I mildly support the change from "port" to "raft", but GREATLY support changes in other SCHEME names. In particular, I find the terms MAPC and MAPCAR offensive. Why not use the names WALK and MAP as is done in T? I like the idea of using english words as names. Thus I prefer LABELS to LETREC. Given that some names will not be english, at the very least, word parts can be separated with dash. LETREC => LET-REC DEFREC => DEF-REC NEWLINE => NEW-LINE As you can guess, CAR and CDR are losers in my book. How about: CAR => -<- CDR => ->- CADR => -<>- CDAR => -><- CxxxR => -xxx- with "A" substituted by "<" and "D" substituted by ">". Bold and wonderful changes from traditional Lisps are being suggested by the Scheme document, for example, dumping the confusion between the empty list and logical false. Now is no time to get stuck with traditional name. John  Received: from SCRC-STONY-BROOK by MIT-MC via Chaosnet; 20 MAR 85 18:36:30 EST Received: from SCRC-RIO-DE-JANEIRO by SCRC-STONY-BROOK via CHAOS with CHAOS-MAIL id 200687; Wed 20-Mar-85 17:11:04-EST Date: Wed, 20 Mar 85 17:11 EST From: Kent M Pitman Subject: Ports and streams To: Scheme@MIT-MC.ARPA cc: JAR@MIT-MC.ARPA In-Reply-To: The message of 20 Mar 85 15:54-EST from Jonathan A Rees Message-ID: <850320171127.2.KMP@RIO-DE-JANEIRO.SCRC.Symbolics.COM> Date: 20 March 1985 15:54-EST From: Jonathan A Rees ... A different metaphor is that a "port" is a sort of a cursor or a marker which moves along a sequence (stream). Well, what moves up and down a sequence or stream? How about "raft"? ... I also dislike "port" for much the same reasons as Jonathan presented. I support the proposal for the new term "raft".  Date: 20 March 1985 15:54-EST From: Jonathan A Rees Subject: Ports and streams To: SCHEME @ MIT-MC I think that we generally agree that were it not for performance reasons, we would have only streams (in the FP/A&S sense), and not ports; am I correct? E.g., a stream object would need to be consed for each character read from or written to a file; this would mean frequent GC's if a lot of file I/O was happening. It's unfortunate that our design is driven by considerations like this, but I guess that's how things are. So the I/O primitives in the essential dialect manipulate things called ports, which are positions on streams of characters. The two notions are equivalent (ports could be defined if the only primitives were stream primitives, and vice versa), so people who care about writing code which does I/O in a pure style can still do so, by implementing I/O streams in terms of I/O ports themselves. So far so good. This message is about terminology, not semantics. I think that "port" is a bad term. The real-life usage of the term "port" (door or harbor) doesn't seem to be a good fit with that of the word "stream". I guess the image that's intended is of a door, with the program on one side and a stream of things on the other, and the things coming into the program's awareness or control through the door. Or perhaps the stream flows through the door? Streams flow under bridges, maybe, or through channels (I don't like "channel" because it has incompatible connotations in computer science), or into aqueducts or over waterfalls, but through doors? The other meaning of "port" doesn't make sense: rivers or possibly streams might flow into harbors or ports, but that's not what makes a port a port; a port is someplace where ships dock, load, etc. So "port" is a very strange word to use in connection with streams. A different metaphor is that a "port" is a sort of a cursor or a marker which moves along a sequence (stream). Well, what moves up and down a sequence or stream? Here are some transportation metaphors: line, screen cursor conduit vehicle rail train road car river boat stream raft How about "raft"? A raft might be an appropriate vehicle for a stream. I guess this is pretty cutesy, but it's an idea. One argument in favor is that "raft" doesn't have connotations in computer science like many other words (port, channel) do. Also, the metaphor is obscure enough that no one would need to know how silly it is; users could imagine that RAFT is an acronym, or some technical term derived from some obscure branch of mathematics or semantics, or whatever. Sounds sort of like "rack" or "rail," terms that certain language designers use without embarrassment. And it's short, a distinct advantage. These two metaphors reflect the difference between a stream and a streambed. If you want to see everything which floats by on a stream, it's sufficient to sit one place (e.g. on a bridge, or on a bank, or at its outlet) and relax and enjoy the view. "Stream" here is the stuff that is flowing. If, however, you want to see all the places that the stream goes, you have to get on a raft or boat or just wade the entire length of the thing from source to outlet. This "stream" is a geographical thing, something you'd find on a map. Besides the fact that "port" is a mixed (or at least poor) metaphor, I am opposed to the term because it reminds me of Franz Lisp. I'd rather not be reminded of Franz every time I use Scheme. I know this is pretty random, but I just wanted to voice my unhappiness and get people thinking about more appropriate terms. Jonathan  Received: from MIT-OZ by MIT-MC via Chaosnet; 20 MAR 85 01:24:39 EST Date: Wed, 20 Mar 1985 01:24 EST Message-ID: From: CPH%MIT-OZ@MIT-MC.ARPA To: Scheme@MIT-MC Subject: Revisions to String Proposal I have revised the earlier proposal I submitted for String operations. The revision was somewhat of a hatchet job, but I think that the important functionality remains. What follows is some notes referring to the earlier proposal. After that I have written a small bit of commentary about mutation of strings. ---------------------------------------------------------------------- Characters The basic operations on characters are the following; they should be essential. (CHAR? ) True iff is a character. (CHAR->INTEGER ) (INTEGER->CHAR ) Maps to coerce characters to integers and vice versa. If two characters have a certain relationship in the character ordering, then the corresponding integers must have the same relationship in the integer ordering. (If I recall right, this means these maps are order isomorphisms between characters and integers.) I would like to propose that the following be essential. These are the case sensitive order predicates for characters; they define the character ordering. I think that the Common Lisp restrictions as to the ordering should be adopted. The names have been changed to conform with Common Lisp, after Don Oxley pointed out that CHAR-EQUAL? was case sensitive, and in CL CHAR-EQUAL was the case insensitive version. I hope that this will clear things up a bit. Each of the following accepts two character objects, and compares them in the obvious way. Optionally, they may take more arguments, as do the corresponding numeric predicates. CHAR=? CHAR? CHAR>=? The following should be optional; they are the case insensitive versions of the above. CHAR-CI=? CHAR-CI? CHAR-CI>=? These character class predicates should be optional, with the meaning described in the Common Lisp manual. They each take one argument, which must be a character. CHAR-UPPER-CASE? CHAR-LOWER-CASE? CHAR-ALPHABETIC? CHAR-NUMERIC? CHAR-ALPHANUMERIC? CHAR-WHITESPACE? CHAR-GRAPHIC? The following should be optional; each takes a character object and returns another character object. They perform case conversion if the argument is lower or upper case, respectively. CHAR-UPCASE CHAR-DOWNCASE ---------------------------------------------------------------------- Strings I think that the following should be essential, as described in my earlier proposal: STRING? MAKE-STRING STRING-LENGTH STRING-REF STRING->LIST LIST->STRING SUBSTRING STRING-APPEND STRING-NULL? Similarly to the transformation for characters, the following should be essential for two arguments, and optionally should take more. STRING=? STRING? STRING>=? Again, these case insensitive versions are optional. STRING-CI=? STRING-CI? STRING-CI>=? Next, these optional procedures provide a small set of operations for mutable strings. STRING-ALLOCATE STRING-COPY STRING-SET! SUBSTRING-FILL! SUBSTRING-MOVE-RIGHT! SUBSTRING-MOVE-LEFT! The remaining operations, while useful, are probably not important enough to standardize on. As I have already demonstrated, all of them can be implemented given the above operations. Here is the corrected text for the -MOVE- operations: (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 starting with the rightmost element, proceeding to the left, while SUBSTRING-MOVE-LEFT! starts with the leftmost element, proceeding to the right. This has the effect that the two operations can be used to shift groups of characters right or left, respectively, within a given string. ---------------------------------------------------------------------- Mutation I have tried to be careful about the issue of mutable strings. None of the operations which I have proposed as "essential" mutate strings. I have provided a small set of operations, marked as "optional", which DO mutate strings. I believe that mutation of strings is basically reasonable; although I understand that in some very important cases, in particular the names of interned symbols, there should be some guarantee that a string cannot be mutated. I believe that this can be solved by one of these simple methods: 1. A string could have an internal bit which, when set, would prevent mutation. 2. There could be two types of strings. In this case, it would be reasonable to decide that the read/print syntax for mutable strings need not be the same as for non-mutable strings. Anyway, I have chosen to have all strings be mutable in the MIT implementation, because that is the simplest choice providing the most power. To the best of my knowledge, no one has ever been screwed by this decision, and it seems unlikely that anyone ever would.  Received: from MIT-OZ by MIT-MC via Chaosnet; 19 MAR 85 17:44:34 EST Date: 19 Mar 1985 17:44 EST (Tue) Message-ID: From: Bill Rozas To: Will Clinger Cc: scheme@MIT-MC.ARPA Subject: Keyboard inputs In-reply-to: Msg of 19 Mar 1985 10:55-EST from Will Clinger I think you don't understand the problem with listen? The system need not have a rubout handler written in Scheme to have an Emacs-like editor. MIT-Scheme has a rubout handler written in Pascal (almost all IO is written in Pascal), and Edwin (Chris Hanson's editor) works perfectly fine, since it bypasses buffered IO. We have two kinds of terminal read-char: One is used by the reader, and is buffered (no characters are given to Scheme until some key (Execute on HP9836, Newline on UNIX) is typed). The other is immediate (it returns as soon as a character is typed, without waiting for any special character). The former is much more efficient under some operating systems, and eliminates the need for a complicated rubout handler since the reader never needs to back out (according to GJS, 1/4 of all the code of MacLisp at some point was the rubout handler). Having an Emacs-like front-end is not incompatible with this, since the front-end need not give any characters to Scheme until no further character-level processing is needed. It is inconvenient, however, to do all input in this mode (I'd hate to have to type a newline after a single character command to a debugger, for example). This is why we have immediate read-char also, which is the default. Users can type asynchronously, and frequently do so ahead of the system. For consistency, the user should be able to edit (especially rub-out) his typeahead until it is read. Since the system does not know whether the next read-char will be immediate or not, it cannot lock the required characters unless it locks all, clearly unpalatable. The problem with listen? arises, for example, from programs which ask for confirmation or a simple choice but assume a default if no input becomes available in some period of time. Assume that the user types a character, the program invokes listen? which returns true, and the user then deletes the character before the program has had a chance to invoke immediate read-char. This is quite likely on a timesharing system, where the user may delete while Scheme is not the current process running. Clearly the program will hang while it should not (it was intended that it "time-out"). Clearly listen? is wothless unless it applies to both liquid and frozen characters (otherwise a terminator would have to be typed after the option, ugh!). The problem is trivially solved if listen? locks the character. I believe that this problem can arise even if there is no buffered io in a system. On Lisp systems that I know (including MacLisp and Lisp Machines), rubout-handling is built into read, not into tyi, and therefore user programs that use tyi rather than read do not (by default) allow users to rub-out their input. If you allow rub-out on read-char throughout the system (which is certainly desirable), the problem potentially exists. Since adding this extra constraint to the definition of listen? solves all these bugs and allows implementors of the io system greater freedom, I think it should be added. On my previous message I advocated for eliminating listen? in favor of non-hanging-read-char, but I drop this. Chris Hanson has pointed out that unless untyi were provided, this would be the cause of pretty ugly code.  Received: from csnet-relay by MIT-MC.ARPA; 19 MAR 85 13:43:27 EST Received: from indiana by csnet-relay.csnet id a008437; 19 Mar 85 13:42 EST Date: Tue, 19 Mar 85 10:55:58 est From: Will Clinger Received: by iuvax.UUCP; id AA03087; Tue, 19 Mar 85 10:55:58 est To: scheme@mit-mc.ARPA Subject: Keyboard inputs > On interactive streams users may have the possibility of >erasing typed characters. What happens if listen? returns true and >then the user erases the character? The program which invoked listen? >will probably attempt to read a character thinking that it will not >hang but it will. We had this bug and went to a fair amount of hair >to get it fixed. This problem doesn't arise if the rubout handler is implemented using READ-CHAR. The system should be designed this way if you expect to implement an Emacs-like editor in Scheme. If the rubout handler is at a lower level than READ-CHAR, then the system is probably set up so that the user first edits her input using rubouts and whatever other facilities are available (cut and paste using a mouse, Emacs, whatever). Let's say that this input is liquid. When the user is satisfied with her input she does something to signal that the input should be sent to Scheme, perhaps by typing the "Execute" key (on the HP 9836) or the "Enter" key (on the Macintosh). At that moment the input should be frozen so that it cannot be edited further. At any given time the input buffer for READ-CHAR may contain older characters that are frozen as well as newer characters that are liquid. READ-CHAR should return only frozen characters, and should hang if there are none. The LISTEN? procedure (or whatever we wind up calling it) should therefore return true only if there are frozen characters in the buffer. It is possible to design a system that can switch back and forth between the first way and the second way. (Unix calls these raw mode and cooked mode.) > We have not talked at all about keyboard interrupts. It >seems to me that any reasonable implementation should provide a >way for the user to (at least) abort an infinite loop. It would >be nice if we had a simple standard mechanism for doing this, but >implementations would be free to have more keyborad interrupts >(we currently have 5 by default). I propose that we choose some >control character (^G, for example) which on all implementations >and unless inhibited (an editor may want to do this) will >interrupt Scheme and make it return to the top-level >read-eval-print loop. Yes, any reasonable implementation must have keyboard interrupts, but we shouldn't standardize on the characters. Consider Scheme 312, which runs under four very different operating systems. Under CP/M-68k, control-G is fine as the interrupt character, and an operating system convention leads users to expect that control-C will leave Scheme for good (it does). Under Un*x, however, users expect SIGINT to be the interrupt character and SIGQUIT to be the abort character; usually these are control-C and control-\, respectively. Under HP Pascal the HP 9836 "Execute" key is rigged to send a control-C; it doesn't make sense for "Execute" to interrupt or abort. The Macintosh doesn't have a control key (the command (cloverleaf) key is something completely different); pull-down menus are probably more appropriate. In short, no matter what conventions we could choose, they would be wrong for some machines.  Received: from CSNET-SH by MIT-MC.ARPA; 19 MAR 85 08:07:47 EST To: scheme@MIT-MC.ARPA Subject: Message a011267 LONG message - part 3 Reply-to: cic@CSNET-SH.ARPA Date: 19 Mar 85 07:53:34 EST (Tue) From: "Charlotte D. Mooers" (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. II.8. Strings Strings are written as sequences of characters enclosed within doublequotes ("). A doublequote can be written inside a string only by escaping it with a backslash (\), as in "The word \"Recursion\" has many different meanings." A backslash can be written inside a string only by escaping it with another backslash. Scheme does not specify the effect of a backslash within a string that is not followed by either a doublequote or another backslash. A string may continue from one line to the next, but this is usually a bad idea because the exact effect varies from one computer system to another. Several of the string procedures involve characters or lists of characters. Characters are written using the #\ notation of Common Lisp. For example: #a ; lower case letter #A ; upper case letter #( ; the left parenthesis as a character # ; the space character #space ; the preferred way to notate a space character #newline ; the newline character The #\ notation is not an essential part of Scheme, however. Even implementations that support the #\ notation for input do not have to support it for output, and there is no requirement that the data type of characters be disjoint from data types such as integers or strings. This section defines no destructive operations on strings, but a much larger collection of string operations including destructive operations has been proposed by Chris Hanson and is under consideration. (string? obj) essential procedure Returns #!true if obj is a string, otherwise returns #!false. (string-equal? string1 string2) essential procedure Returns #!true if string1 and string2 are strings of the same length and have the same characters in the same positions. (string-less? string1 string2) essential procedure Returns #!true if string1 is lexicographically less than string2. The ordering defined by string-less? varies from one computer system to another, but it is always an irreflexive total ordering on strings. (string-length string) essential procedure Returns the number of characters in string. (string-ref string k) essential procedure k must be a nonnegative integer less than the string-length of string. Returns character k of string using zero-origin addressing. (string-ref "abcde" 2) --> #c (string-append string1 string2) essential procedure (string-append string ...) procedure Returns a string whose characters form the catenation of the given strings. (string->list string) essential procedure Returns a list of the characters that make up string. See list->string. (list->string char-list) essential procedure char-list must be a proper list of characters. Returns a string formed from the characters in char-list. list->string and string->list are inverses so far as equal? is concerned. II.9. Vectors Vectors are heterogenous mutable structures whose elements are indexed by integers. The first element in a vector is indexed by zero, and the last element is indexed by one less than the length of the vector. A vector of length 3 containing the number 0 in element 0, the list (2 2 2 2) in element 1, and the string "Anna" in element 2 can be written as #(0 (2 2 2 2) "Anna") Vectors are created by the constructor procedure make-vector. The elements are accessed and assigned by the procedures vector-ref and vector-set!. (vector? obj) essential procedure Returns #!true if obj is a vector, otherwise returns #!false. (make-vector size) essential procedure (make-vector size fill) procedure Returns a newly allocated vector of size elements. If a second argument is given, then each element is initialized to fill. Otherwise the initial contents of each element is unspecified. (vector obj ...) essential procedure Returns a newly allocated vector whose elements contain the given arguments. Analogous to list. (vector 'a 'b 'c) --> #(a b c) (vector-length vec) essential procedure Returns the number of elements in the vector vec. (vector-ref vec k) essential procedure Returns the contents of element k of the vector vec. k must be a nonnegative integer less than the vector-length of vec. (vector-ref '#(1 1 2 3 5 8 13 21) 5) --> 8 (vector-set! vec k obj) essential procedure Stores obj in element k of the vector vec. k must be a nonnegative integer less than the vector-length of vec. The value returned by vector-set! is not specified. (define vec '#(0 (2 2 2 2) "Anna")) --> vec (vector-set! vec 1 '("Sue" "Sue")) --> unspecified vec --> #(0 ("Sue" "Sue") "Anna") (vector->list vec) essential procedure Returns a list of the objects contained in the elements of vec. See list->vector. (vector->list '#(dah dah didah)) --> (dah dah didah) (list->vector elts) essential procedure Returns a newly created vector whose elements are initialized to the elements of the proper list elts. (list->vector '(dididit dah)) --> #(dididit dah) (vector-fill! vec fill) procedure Stores fill in every element of the vector vec. The value returned by vector-fill! is not specified. II.10. The object table (object-hash obj) procedure (object-unhash n) procedure object-hash associates an integer with obj in a global table and returns obj. object-hash guarantees that distinct objects (in the sense of eq?) are associated with distinct integers. object-unhash takes an integer and returns the object associated with that integer if there is one, returning false otherwise. Rationale: object-hash and object-unhash can be implemented using association lists and the assq procedure, but the intent is that they be efficient hash functions for general objects. II.11. Procedures Procedures are created when lambda expressions are evaluated. Procedures do not have a standard printed representation because recursively defined procedures are conceptually infinite and may be implementationally circular. The most common thing to do with a procedure is to call it with zero or more arguments. A Scheme procedure may also be stored in data structures or passed as an argument to procedures such as those described below. (apply proc args) essential procedure (apply proc arg1 ... args) procedure proc must be a procedure and args must be a proper list of arguments. The first (essential) form calls proc with the elements of args as the actual arguments. The second form is a generalization of the first that calls proc with the elements of (append (list arg1 ...) args) as the actual arguments. (apply + (list 3 4)) --> 7 (define compose (lambda (f g) (lambda args (f (apply g args))))) --> compose ((compose 1+ *) 3 4) --> 13 (mapcar f plist) essential procedure (mapcar f plist1 plist2 ...) procedure f must be a procedure of one argument and the plists must be proper lists. If more than one plist is given, then the plists should all be the same length. Applies f element-wise to the elements of the plists and returns a list of the results. The order in which f is applied to the elements of the plists is not specified. (mapcar cadr '((a b) (d e) (g h))) --> (b e h) (mapcar (lambda (n) (expt n n)) '(1 2 3 4 5)) --> (1 4 27 256 3125) (mapcar + '(1 2 3) '(4 5 6)) --> (5 7 9) (define count 0) --> unspecified (mapcar (lambda (ignored) (set! count (1+ count)) count) '(a b c)) --> unspecified The mapcar procedure often goes by the name of map in other advanced programming languages. (mapc f plist) essential procedure (mapc f plist1 plist2 ...) procedure The arguments to mapc are like the arguments to mapcar, but mapc calls f for its side effects rather than for its values. Unlike mapcar, mapc is guaranteed to call f on the elements of the plists in order from the first element to the last, and the value returned by mapc is not specified. (define v (make-vector 5)) --> v (mapc (lambda (i) (vector-set! v i (* i i))) '(0 1 2 3 4)) --> unspecified v --> #(0 1 4 9 16) The name of mapc is traditional and cannot otherwise be defended. (call-with-current-continuation f) procedure The classic use of call-with-current-continuation is for structured, non-local exits from loops or procedure bodies, but in fact call-with-current-continuation is extremely useful for implementing a wide variety of advanced control structures. Whenever a Scheme expression is evaluated there is a continuation wanting the result of the expression. The continuation represents an entire future for the computation. If the expression is evaluated at top level, for example, then the continuation will take the result, print it on the screen, prompt for the next input, evaluate it, and so on forever. Most of the time the continuation includes actions specified by user code, as in a continuation that will take the result, multiply it by the value stored in a local variable, add seven, and give the answer to the top level continuation to be printed. Normally these ubiquitous continuations are hidden behind the scenes and programmers don't think much about them. On rare occasions, however, when programmers need to do something fancy, then they may need to deal with continuations explicitly. call-with-current-continuation allows Scheme programmers to create a procedure that acts just like the current continuation. f must be a procedure of one argument. call-with-current-continuation packages up the current continuation as an "escape procedure" and passes it as an argument to f. The escape procedure is an ordinary Scheme procedure of one argument that, if it is later passed a value, will ignore whatever continuation is in effect at that later time and will give the value instead to the continuation that was in effect when the escape procedure was created. The escape procedure created by call-with-current-continuation has unlimited extent just like any other procedure in Scheme. It may be stored in variables or data structures and may be called as many times as desired. The following examples show only the most common uses of call-with-current-continuation. If all real programs were as simple as these examples, there would be no need for a procedure with the power of call-with-current-continuation. (call-with-current-continuation (lambda (exit) (mapc (lambda (x) (if (negative? x) (exit x))) '(54 0 37 -3 245 19)) #!true)) --> -3 (define list-length (lambda (obj) (call-with-current-continuation (lambda (return) ((rec loop (lambda (obj) (cond ((null? obj) 0) ((pair? obj) (1+ (loop (cdr obj)))) (else (return #!false))))) obj))))) --> list-length (list-length '(1 2 3 4)) --> 4 (list-length '(a b . c)) --> #!false Rationale: Most serious programming languages incorporate one or more special purpose escape constructs with names like exit, return, or even goto. In 1965, however, Peter Landin invented a general purpose escape operator called the J-operator. John Reynolds described a simpler but equally powerful construct in 1972. The catch special form described by Sussman and Steele in the 1975 report on Scheme is exactly the same as Reynolds's construct, though its name came from a less general construct in MacLisp. The fact that the full power of Scheme's catch could be obtained using a procedure rather than a special form was noticed in 1982 by the implementors of Scheme 311, and the name call-with-current-continuation was coined later that year. Some people think the name is good because its length discourages programmers from using the procedure casually; others have taken to calling the procedure call/cc. II.12. Ports Ports represent input and output devices. To Scheme, an input device is a Scheme object that can deliver characters upon command, while an output device is a Scheme object that can accept characters. (call-with-input-file string proc) essential procedure (call-with-output-file string proc) essential procedure proc is a procedure of one argument, and string is a string naming a file. For call-with-input-file, the file must already exist; for call-with-output-file, the effect is unspecified if the file already exists. Calls proc with one argument: the port obtained by opening the named file for input or output. If the file cannot be opened, an error is signalled. If the procedure returns, then the port is closed automatically and the value yielded by the procedure is returned. If the current continuation ever changes in such a way as to make it doubtful that the procedure will return, the port may be closed automatically, but the exact interaction with escape procedures is unspecified. Rationale: Whether or not the port is closed when the procedure does not return 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. The extreme generality of Scheme's escape procedures makes it impossible to know for certain whether a procedure will return, and procedures can in fact return more than once. (input-port? obj) essential procedure (output-port? obj) essential procedure Returns #!true if obj is an input port or output port (respectively), otherwise returns #!false. (current-input-port) essential procedure (current-output-port) essential procedure Returns the current default input or output port. (with-input-from-file string thunk) procedure (with-output-to-file string thunk) procedure thunk is a procedure of no arguments, and string is a string naming a file. For with-input-from-file, the file must already exist; for with-output-to-file, the effect is unspecified if the file already exists. The file is opened for input or output, an input or output port connected to it is made the default value returned by current-input-port or current-output-port, and the thunk is called with no arguments. When the thunk returns, the port is closed and the previous default is restored. with-input-from-file and with-output-to-file return the value yielded by thunk. Furthermore these procedures will attempt to close the default port and restore the previous default whenever the current continuation changes in such a way as to make it doubtful that the thunk will ever return. See call-with-input-file and call-with-output-file. (open-input-file filename) procedure Takes a string naming an existing file and returns an input port capable of delivering characters from the file. If the file cannot be opened, an error is signalled. (open-output-file filename) procedure Takes a string naming an output file to be created and returns an output port capable of writing characters to a new file by that name. If the file cannot be opened, an error is signalled. If a file with the given name already exists, the effect is unspecified. (close-input-port port) procedure (close-output-port port) procedure Closes the file associated with port, rendering the port incapable of delivering or writing characters. The value returned is not specified. II.13. Input The read procedure converts written representations of Scheme objects into the objects themselves. The written representations for Scheme objects are described in the sections devoted to the operations on those objects, and the grubby details of lexical syntax are described in an appendix. (eof? obj) essential procedure Returns true iff obj 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 objects will ever be a character or an object that can be read in using read. (read) essential procedure (read port) essential procedure Returns the next object parsable from the given input port, updating port 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 is signalled. The port argument may be omitted, in which case it defaults to the value returned by current-input-port. Rationale: This corresponds to Common Lisp's read-preserving-whitespace, but for simplicity it is never an error to encounter end of file except in the middle of an object. (read-char) essential procedure (read-char port) essential procedure Reads the next character available from the input port, updating the port to point to the following character. If no more characters are available, an end of file object is returned. port may be omitted, in which case it defaults to the value returned by current-input-port. (listen?) procedure (listen? port) procedure Returns true if a character is ready on the input port so that a read-char operation will not hang, and returns false otherwise. If the port is at end of file then the value returned by listen? is unspecified. port may be omitted, in which case it defaults to the value returned by current-input-port. (load filename) essential procedure filename should be a string naming an existing file containing Scheme source code. The load procedure reads expressions from the file and evaluates them sequentially as though they had been typed interactively. It is not specified whether the results of the expressions are printed, however, nor is it specified whether 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. II.14. Output (write obj) essential procedure (write obj port) essential procedure Writes a representation of obj to the given port. Strings that appear in the written representation are enclosed in doublequotes, and within those strings backslash and doublequote characters are escaped by backslashes. write returns an unspecified value. The port argument may be omitted, in which case it defaults to the value returned by current-output-stream. See display. (display obj) essential procedure (display obj port) essential procedure Writes a representation of obj to the given port. Strings that appear in the written representation are not enclosed in doublequotes, and no characters are escaped within those strings. display returns an unspecified value. The port argument may be omitted, in which case it defaults to the value returned by current-output-stream. See write. Rationale: Like Common Lisp's prin1 and princ, write is for producing machine-readable output and display is for producing human-readable output. Implementations that allow "slashification" within symbols will probably want write but not display to slashify funny characters in symbols. (newline) essential procedure (newline port) essential procedure Writes an end of line to port. Exactly how this is done differs from one operating system to another. Returns an unspecified value. The port argument may be omitted, in which case it defaults to the value returned by current-output-port. (write-char char) essential procedure (write-char char port) essential procedure Writes the character char (not a written representation of the character) to the given port and returns an unspecified value. The port argument may be omitted, in which case it defaults to the value returned by current-output-port. (transcript-on filename) procedure (transcript-off) procedure filename must be a string naming an output file to be created. 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, which closes the transcript file. Only one transcript may be in progress at any time, though some implementations may relax this restriction. The values returned by these procedures are unspecified. Rationale: These procedures are redundant in some systems, but systems that need them should provide them. References Harold Abelson and Gerald Jay Sussman with Julie Sussman, Structure and Interpretation of Computer Programs, MIT Press, 1985. William Clinger, "The Scheme 311 compiler: an exercise in denotational semantics", Conference Record of the 1984 ACM Symposium on Lisp and Functional Programming, August 1984, pages 356-364. Carol Fessenden, William Clinger, Daniel P Friedman, and Christopher Haynes, "Scheme 311 version 4 reference manual", Indiana University Computer Science Technical Report 137, February 1983. D Friedman, C Haynes, E Kohlbecker, and M Wand, "Scheme 84 interim reference manual", Indiana University Computer Science Technical Report 153, January 1985. Christopher T Haynes, Daniel P Friedman, and Mitchell Wand, "Continuations and coroutines", Conference Record of the 1984 ACM Symposium on Lisp and Functional Programming, August 1984, pages 293-298. Peter Landin, "A correspondence between Algol 60 and Church's lambda notation: Part I", Communications of the ACM 8, 2, February 1965, pages 89-101. MIT Scheme Manual, Seventh Edition, September 1984. Peter Naur et al, "Revised report on the algorithmic language Algol 60", Communications of the ACM 6, January 1963, pages 1-17. Kent M Pitman, The Revised MacLisp Manual, MIT Artificial Intelligence Laboratory Technical Report 295, 21 May 1983 (Saturday Evening Edition). Jonathan A Rees, Norman I Adams, James R Meehan, "The T manual", Fourth Edition, 10 January 1984. John Reynolds, "Definitional interpreters for higher order programming languages", ACM Conference Proceedings, 1972, pages 717-740. Guy Lewis Steele Jr and Gerald Jay Sussman, "The revised report on Scheme, a dialect of Lisp", MIT Artificial Intelligence Laboratory Memo 452, January 1978. Guy L Steele, Rabbit: a compiler for Scheme, MIT Artificial Intelligence Laboratory Technical Report 474, May 1978. Guy L Steele, "An overview of Common Lisp", Conference Record of the 1982 ACM Symposium on Lisp and Functional Programming, August 1982, pages 98-107. Guy Lewis Steele Jr, Common Lisp: the Language, Digital Press, 1984. Gerald Jay Sussman and Guy Lewis Steele Jr, "Scheme: an interpreter for extended lambda calculus", MIT Artificial Intelligence Laboratory Memo 349, December 1975. Gerald Jay Sussman, Jack Holloway, Guy L Steele, and Alan Bell, "Scheme-79 -- Lisp on a chip", IEEE Computer 14, 7, July 1981, pages 10-21.  Received: from CSNET-SH by MIT-MC.ARPA; 19 MAR 85 07:56:39 EST To: scheme@MIT-MC.ARPA Subject: Message a011267 LONG message - part 2 Reply-to: cic@CSNET-SH.ARPA Date: 19 Mar 85 07:51:18 EST (Tue) From: "Charlotte D. Mooers" user can distinguish obj1 and obj2, then eq? will return #!false. On the other hand, it is guaranteed that objects maintain their identity despite being fetched from or stored into variables or data structures. The notion of identity used by eq? is stronger than the notions of equivalence used by the eqv? and equal? predicates. The constants #!true and #!false are identical to themselves and are different from everything else, except that in some implementations the empty list is identical to #!false for historical reasons. Two symbols are identical if they print the same way (except that some implementations may have "uninterned symbols" that violate this rule). For structured objects such as pairs and vectors the notion of sameness is defined in terms of the primitive mutation procedures defined on those objects. For example, two pairs are the same if and only if a set-car! operation on one changes the car field of the other. The rules for identity of numbers are extremely implementation-dependent and should not be relied on. Generally speaking, the equal? procedure should be used to compare lists, vectors, and arrays. The string-equal? procedure should be used to compare strings, and the =? procedure should be used to compare numbers. The eqv? procedure is just like eq? except that it is more inclined to say that two numbers are the same. (eq? 'a 'a) --> #!true (eq? 'a 'b) --> #!false (eq? '(a) '(a)) --> unspecified (eq? "a" "a") --> unspecified (eq? 2 2) --> unspecified (eq? (cons 'a 'b) (cons 'a 'b)) --> #!false (let ((x (read))) (eq? (cdr (cons 'b x)) x))) --> #!true (eqv? obj1 obj2) essential procedure eqv? is just like eq? except that if obj1 and obj2 are exact numbers then eqv? is guaranteed to return #!true if obj1 and obj2 are equal according to the =? procedure. (eq? 100000 100000) --> unspecified (eqv? 100000 100000) --> #!true See section II.6 for a discussion of exact numbers. (equal? obj1 obj2) essential procedure Returns #!true if obj1 and obj2 are identical objects or if they are equivalent numbers, lists, strings, vectors, or arrays. Two objects are generally considered equivalent if they print the same. equal? may not terminate if its arguments are circular data structures. (equal? 'a 'a) --> #!true (equal? '(a) '(a)) --> #!true (equal? '(a (b) c) '(a (b) c)) --> #!true (equal? "abc" "abc") --> #!true (equal? 2 2) --> #!true (equal? (make-vector 5 'a) (make-vector 5 'a)) --> #!true equal? is a blunderbuss of a predicate and should not be used when a less liberal or more specific predicate will suffice. II.4. Pairs and lists Lists are Lisp's -- and therefore Scheme's -- characteristic data structures. Even Lisp and Scheme programs are lists. The empty list is a special object that is written as an opening parenthesis followed by a closing parenthesis: () The empty list has no elements, and its length is zero. The empty list is not a pair. Larger lists are built out of pairs. A pair (sometimes called a "dotted pair") is a record structure with two fields called the car and cdr fields (for historical reasons). Pairs are created by the constructor procedure cons. The car and cdr fields are accessed by the selector procedures car and cdr. The car and cdr fields are assigned by the mutator procedures set-car! and set-cdr!. The most general notation used for Scheme pairs is the "dotted" notation (c1 . c2) where c1 is the value of the car field and c2 is the value of the cdr field. For example (4 . 5) is a pair whose car is 4 and whose cdr is 5. The dotted notation is not often used, because more streamlined notations exist for the common case where the cdr is the empty list or a pair. (c1 . ()) is usually written as (c1). (c1 . (c2 . c3)) is usually written as (c1 c2 . c3). Usually these special notations permit a structure to be written without any dotted pair notation at all. For example (a . (b . (c . (d . (e . ()))))) would normally be written as (a b c d e) When all the dots can be made to disappear as in the example above, the entire structure is called a proper list. Proper lists are so common that when people speak of a list, they usually mean a proper list. For those who prefer an inductive definition: 1. The empty list is a proper list. 2. If l is a proper list, then any pair whose cdr is l is also a proper list. 3. There are no other proper lists. A proper list is therefore either the empty list or a pair from which the empty list can be obtained by applying the cdr procedure a finite number of times. Whether a given pair is a proper list depends upon what is stored in the cdr field. When the set-cdr! procedure is used, an object can be a proper list one moment and not the next: (define x '(a b c)) --> x (define y x) --> y (set-cdr! x 4) --> unspecified x --> (a . 4) (eq? x y) --> #!true y --> (a . 4) A pair object, on the other hand, will always be a pair object. It is often convenient to speak of a homogeneous (proper) list of objects of some particular data type, as for example (1 2 3) is a list of integers. To be more precise, suppose D is some data type. (Any predicate defines a data type consisting of those objects of which the predicate is true.) Then 1. The empty list is a list of D. 2. If l is a list of D, then any pair whose cdr is l and whose car satisfies the data type D is also a list of D. 3. There are no other lists of D. (pair? obj) essential procedure Returns #!true if obj is a pair, otherwise returns #!false. (pair? '(a . b)) --> #!true (pair? '(a b c)) --> #!true (pair? '()) --> #!false (pair? '#(a b)) --> #!false (cons obj1 obj2) essential procedure Returns a newly allocated pair whose car is obj1 and whose cdr is obj2. The pair is guaranteed to be different (in the sense of eq?) from every existing object. (cons 'a '()) --> (a) (cons '(a) '(b c d)) --> ((a) b c d) (cons "a" '(b c)) --> ("a" b c) (cons 'a 3) --> (a . 3) (cons '(a b) 'c) --> ((a b) . c) (car pair) essential procedure Returns the contents of the car field of pair. pair must be a pair. Note that taking the car of the empty list is an error. (car '(a b c)) --> a (car '((a) b c d)) --> (a) (car '(1 . 2)) --> 1 (car '()) --> error (cdr pair) essential procedure Returns the contents of the cdr field of pair. pair must be a pair. Note that taking the cdr of the empty list is an error. (cdr '((a) b c d)) --> (b c d) (cdr '(1 . 2)) --> 2 (cdr '()) --> error (set-car! pair obj) essential procedure Stores obj in the car field of pair. pair must be a pair. The value returned by set-car! is unspecified. This procedure can be very confusing if used indiscriminately. (set-cdr! pair obj) essential procedure Stores obj in the cdr field of pair. pair must be a pair. The value returned by set-cdr! is unspecified. This procedure can be very confusing if used indiscriminately. (caar pair) essential procedure (cadr pair) essential procedure (cdar pair) essential procedure (cddr pair) essential procedure (caaar pair) essential procedure (caadr pair) essential procedure (cadar pair) essential procedure (caddr pair) essential procedure (cdaar pair) essential procedure (cdadr pair) essential procedure (cddar pair) essential procedure (cdddr pair) essential procedure (caaaar pair) essential procedure (caaadr pair) essential procedure (caadar pair) essential procedure (caaddr pair) essential procedure (cadaar pair) essential procedure (cadadr pair) essential procedure (caddar pair) essential procedure (cadddr pair) essential procedure (cdaaar pair) essential procedure (cdaadr pair) essential procedure (cdadar pair) essential procedure (cdaddr pair) essential procedure (cddaar pair) essential procedure (cddadr pair) essential procedure (cdddar pair) essential procedure (cddddr pair) essential procedure These procedures are compositions of car and cdr, where for example caddr could be defined by (define caddr (lambda (x) (car (cdr (cdr x))))) '() essential constant #!null constant () is the empty list. The #!null notation does not have to be quoted in programs. The '() notation must include the quote, however, because otherwise it would be a procedure call without a procedure expression. Rationale: Because many current Scheme interpreters deal with expressions as list structures rather than as character strings, they will treat an unquoted () as though it were quoted. It is entirely possible, however, that some implementations of Scheme will be able to detect this error. (null? obj) essential procedure Returns #!true if obj is the empty list, otherwise returns #!false. (list obj1 ...) essential procedure Returns a list of its arguments. (list 'a (+ 3 4) 'c) --> (a 7 c) (length plist) essential procedure Returns the length of plist, which must be a proper list. (length '()) --> 0 (length '(a b c)) --> 3 (length '(a (b) (c d e))) --> 3 (append plist1 plist2) essential procedure (append plist ...) procedure Returns a list consisting of the elements of the first plist followed by the elements of the other plists. All plists should be proper lists. (append '(x) '(y)) --> (x y) (append '(a) '(b c d)) --> (a b c d) (append '(a (b)) '((c))) --> (a (b) (c)) (append! plist ...) procedure Like append but may side effect its arguments. (reverse plist) procedure Returns a list consisting of the elements of plist in reverse order. plist must be a proper list. (reverse '(a b c)) --> (c b a) (reverse '(a (b c) d (e (f)))) --> ((e (f)) d (b c) a) (list-ref x n) procedure Returns the car of (list-tail x n). (list-tail x n) procedure Returns the sublist of x obtained by omitting the first n elements. Could be defined by (define list-tail (lambda (x n) (if (zero? n) x (list-tail (cdr x) (- n 1))))) (last-pair x) procedure Returns the last pair in the nonempty list x. Could be defined by (define last-pair (lambda (x) (if (pair? (cdr x)) (last-pair (cdr x)) x))) (memq obj plist) essential procedure (memv obj plist) essential procedure (member obj plist) essential procedure Finds the first occurrence of obj in the proper list plist and returns the first sublist of plist beginning with obj. If obj does not occur in plist, returns #!false. memq uses eq? to compare obj with the elements of plist, while memv uses eqv? and member uses equal?. (memq 'a '(a b c)) --> (a b c) (memq 'b '(a b c)) --> (b c) (memq 'a '(b c d)) --> #!false (memq (list 'a) '(b (a) c)) --> #!false (memq 101 '(100 101 102)) --> unspecified (memv 101 '(100 101 102)) --> (101 102) (member (list 'a) '(b (a) c)) --> ((a) c) (assq obj alist) essential procedure (assv obj alist) essential procedure (assoc obj alist) essential procedure alist must be a proper list of pairs. Finds the first pair in alist whose car field is obj and returns that pair. If no pair in alist has obj as its car, returns #!false. assq uses eq? to compare obj with the car fields of the pairs in alist, while assv uses eqv? and assoc uses equal?. (assq 'a '((a 1) (b 2) (c 3))) --> (a 1) (assq 'b '((a 1) (b 2) (c 3))) --> (b 2) (assq 'd '((a 1) (b 2) (c 3))) --> #!false (assq '(a) '(((a)) ((b)) ((c)))) --> #!false (assq 5 '((2 3) (5 7) (11 13))) --> unspecified (assv 5 '((2 3) (5 7) (11 13))) --> (5 7) (assoc '(a) '(((a)) ((b)) ((c)))) --> ((a)) Rationale: memq, memv, member, assq, assv, assoc do not have question marks in their names because they return useful values rather than just #!true. II.5. Symbols Symbols are objects whose usefulness rests entirely on the fact that two symbols are identical (in the sense of eq?) if and only if their names are spelled the same way. This is exactly the property that is needed for identifiers in programs, and so most implementations of Scheme use them internally for that purpose. Programmers may also use symbols as they use enumerated types in Pascal. The rules for writing a symbol are the same as the rules for writing an identifier (see section I.2). As with identifiers, different implementations of Scheme use slightly different rules, but it is always the case that a sequence of characters that contains no special characters and begins with a character that cannot begin a number is taken to be a symbol; in addition +, -, 1+, and -1+ are symbols. The case in which a symbol is written in unimportant. Some implementations of Scheme convert any upper case letters to lower case, and others convert lower case to upper case. It is guaranteed that any symbol that has been read using the read procedure and subsequently written out using the write procedure will read back in as the identical symbol (in the sense of eq?). The string->symbol procedure, however, can create symbols for which the write/read invariance may not hold because their names contain special characters or letters in the non-standard case. Rationale: Some implementations of Lisp have a feature known as "slashification" in order to guarantee write/read invariance for all symbols, but historically the most important use of this feature has been to compensate for the lack of a string data type. Some implementations have "uninterned symbols", which defeat write/read invariance even in implementations with slashification and also generate exceptions to the rule that two symbols are the same if and only if their names are spelled the same. It is questionable whether these features are worth their complexity, so they are not standard in Scheme. (symbol? obj) essential procedure Returns #!true is obj is a symbol, otherwise returns #!false. (symbol? 'foo) --> #!true (symbol? (car '(a b))) --> #!true (symbol? "bar") --> #!false (symbol->string symbol) essential procedure Returns the name of symbol as a string. symbol->string performs no case conversion. See string->symbol. The following examples assume the read procedure converts to lower case: (symbol->string 'flying-fish) --> "flying-fish" (symbol->string 'Martin) --> "martin" (symbol->string (string->symbol "Malvina")) --> "Malvina" (string->symbol string) essential procedure Returns the symbol whose name is string. string->symbol can create symbols with special symbols or upper case letters in their names, but it is usually a bad idea to create such symbols because in some implementations of Scheme they cannot be read as themselves. See symbol->string. 'mISSISSIppi --> mississippi (string->symbol "mISSISSIppi") --> mISSISSIppi (eq? 'bitBlt (string->symbol "bitBlt")) --> #!false (eq? 'JollyWog (string->symbol (symbol->string 'JollyWog))) --> #!true (string-equal? "K. Harper, M.D." (symbol->string (string->symbol "K. Harper, M.D."))) --> #!true II.6. Numbers (part 1) 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 execute numerical code efficiently. 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's 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. Notations for numbers are the subject of section II.7. 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 sizes of 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), and 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 manipulating numbers. In general, numerical operations require numerical arguments. For succintness we let the following meta-symbols range over the indicated types of object in our descriptions, and we let these meta-symbols specify the types of the arguments to numeric operations. 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 (number? obj) essential procedure (complex? obj) essential procedure (real? obj) essential procedure (rational? obj) essential procedure (integer? obj) essential procedure These 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 that has only INTEGERs. Nonetheless every implementation of Scheme must have all of these predicates. (zero? z) essential procedure (positive? x) essential procedure (negative? x) essential procedure (odd? n) essential procedure (even? n) essential procedure (exact? z) essential procedure (inexact? z) essential procedure These numerical predicates test a number for a particular property. They return a boolean value. (= z1 z2) essential procedure (=? z1 z2) essential procedure (= z1 z2 z3 ...) procedure (=? z1 z2 z3 ...) procedure (< x1 x2) essential procedure ( x1 x2) essential procedure (>? x1 x2) essential procedure (> x1 x2 x3 ...) procedure (>? x1 x2 x3 ...) procedure (<= x1 x2) essential procedure (<=? x1 x2) essential procedure (<= x1 x2 x3 ...) procedure (<=? x1 x2 x3 ...) procedure (>= x1 x2) essential procedure (>=? x1 x2) essential procedure (>= x1 x2 x3 ...) procedure (>=? x1 x2 x3 ...) procedure The numerical comparison predicates have redundant names (with and without the terminal "?") to make all populations happy. They optionally take many arguments, as in Common Lisp, to facilitate range checks. These procedures return true if their arguments are (respectively): numerically equal, monotonically increasing, monotonically decreasing, monotonically nondecreasing, or monotonically nonincreasing. Warning: On INEXACT numbers equality tests will give unreliable results; other numerical comparisons are only heuristically useful (ask a numerical analyst about this!). (max x1 x2) essential procedure (max x1 x2 ...) procedure (min x1 x2) essential procedure (min x1 x2 ...) procedure Returns the maximum or minimum of its arguments, respectively. (+ z1 z2) essential procedure (+ z1 ...) procedure (* z1 z2) essential procedure (* z1 ...) procedure These procedures return the sum or product of their arguments. (+ 3 4) --> 7 (+ 3) --> 3 (+) --> 0 (* 4) --> 4 (*) --> 1 (- z1 z2) essential procedure (- z1 z2 ...) procedure (/ z1 z2) essential procedure (/ z1 z2 ...) procedure With two or more arguments, these procedures return the difference or (complex) quotient of their arguments, associating to the left. With one argument, however, they return the additive or multiplicative inverse of their argument. (- 3 4) --> -1 (- 3 4 5) --> -6 (- 3) --> -3 (/ 3 4 5) --> 3/20 (/ 3) --> 1/3 (1+ z) procedure (-1+ z) procedure These procedures return the result of adding or subtracting 1 to their argument. (abs z) essential procedure Returns the magnitude of its argument. (abs -7) --> 7 (abs -3+4i) --> 5 (quotient n1 n2) essential procedure (remainder n1 n2) essential procedure (modulo n1 n2) procedure In general, these are intended to implement number-theoretic (integer) division, where for positive integers n1 and n2 if n3 and n4 are integers such that n1 = n3 * n2 + n4 and 0 <= n4 < n2 then (quotient n1 n2) --> n3 (remainder n1 n2) --> n4 (modulo n1 n2) --> n4 remainder and modulo differ on negative arguments as do 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 (gcd n1 ...) procedure (lcm n1 ...) procedure These procedures return the greatest common divisor or least common multiple of their arguments. The arguments to lcm should be nonzero. The result is always non-negative. (gcd 32 -36) --> 4 (gcd) --> 0 (lcm 32 -36) --> 288 (lcm) --> 1 (floor x) procedure (ceiling x) procedure (truncate x) procedure (round x) procedure (rationalize x y) procedure (rationalize x) procedure These procedures create integers and rationals. Their results are not EXACT -- in fact, their results are clearly INEXACT, though they can be made EXACT with an explicit exactness coercion. floor returns the largest integer not larger than x. ceiling returns the smallest integer not smaller than x. truncate returns the integer of maximal absolute value not larger than x. round returns the closest integer to x, rounding to even when x is halfway between two integers. With two arguments, rationalize produces the best rational approximation to x within the tolerance specified by y. With one argument, rationalize produces the best rational approximation to x, preserving all of the precision in its representation. (exp z) procedure (log z) procedure (expt z1 z2) procedure (sqrt z) procedure (sin z) procedure (cos z) procedure (tan z) procedure (asin z) procedure (acos z) procedure (atan z1 z2) procedure These procedures are part of every implementation that supports real numbers. Their meanings conform with the Common Lisp standard. (Be careful of the branch cuts if complex numbers are allowed.) (make-rectangular x1 x2) procedure (make-polar x3 x4) procedure (real-part z) procedure (imag-part z) procedure (magnitude z) procedure (angle z) procedure These procedures are part of every implementation that supports complex numbers. Suppose x1, x2, x3, and x4 are real numbers and z is a complex number such that z = x1 + x2 i = x3 * e ^ (x4 i) Then make-rectangular and make-polar return z, real-part returns x1, imag-part returns x2, magnitude returns x3, and angle returns x4. (exact->inexact z) procedure (inexact->exact z) procedure exact->inexact returns an inexact representation of z, which is a pretty harmless thing to do. inexact->exact returns an exact representation of z; be sure you know what you are doing here! II.7. Numbers (part 2) Numerical Input and Output. Scheme allows all of the traditional ways of expressing numerical constants, though any particular implementation may support only some of them. 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 number format) procedure 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 string exactness radix) procedure The exactness is a symbol, either E (for EXACT) or I (for INEXACT). The radix is also a symbol: B for binary, O for octal, D for decimal, and X for hexadecimal. Returns a number parsed from the string. 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) 12345678901234567890123456789 ; A big one! 355/113 -355/113 +355/113 ; (RAT) +123.45 -123.45 ; (FIX 2) 3.14159265358979 ; (FIX 14) 3.14159265358979 ; (FLO 15) 123.450 ; (FLO 6) -123.45E-1 ; (SCI 5 2) 123E3 123E-3 -123E-3 ; (SCI 3 0) -1+2i ; (RECT (INT) (INT)) 1.2@1.570796 ; (POLAR (FIX 1) (FLO 7)) 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))) #o1.2@1.570796327 ; (POLAR (FLO 2 (RADIX O)) (FLO (RADIX D S)) 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 that 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 the 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 the 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  Received: from CSNET-SH by MIT-MC.ARPA; 19 MAR 85 07:56:10 EST To: scheme@MIT-MC.ARPA Subject: Message a011267 LONG message - part 1 Reply-to: cic@CSNET-SH.ARPA Date: 19 Mar 85 07:48:45 EST (Tue) From: "Charlotte D. Mooers" Received: from csnet-relay.arpa by CSNET-SH.ARPA id a028166; 18 Mar 85 13:26 EST Date: Mon, 18 Mar 85 10:55:42 EST From: CSNET-RELAY Memo Service (MMDF) Subject: Failed mail (msg.a011267) Sender: mmdf@CSNET-RELAY.ARPA To: Postmaster@CSNET-RELAY.ARPA Your message could not be delivered to 'scheme@mit-mc.ARPA (host: mit-mc.arpa) (queue: smtp)' for the following reason: ' Message is too large to mail; use FTP.' Your message follows: Received: from indiana by csnet-relay.csnet id a011267; 18 Mar 85 9:36 EST Date: Mon, 18 Mar 85 01:24:29 est From: willc@Indiana (Will Clinger) Received: by iuvax.UUCP; id AA06606; Mon, 18 Mar 85 01:24:29 est To: scheme@mit-mc Subject: DRAFT of Revised Revised Report (LONG message) Here is the DRAFT of the final report on the Brandeis workshop. It is missing all its appendices, and a couple of sections that deal with the lexical syntax of numbers need more work. Please read it and tell me what you think. We'd like to have your comments by 1 April so we can send it to press. The font information has been stripped for posting to scheme@mit-mc. Some ambiguities probably result, but please don't get upset about them. If you have anything more to say about Chris Hanson's work on strings, please say it soon. We'd like to include Chris's work in this report. -- Will Clinger willc@indiana Table of Contents Part I: Introduction to Scheme I.0 Brief history of Scheme I.1 Syntax of Scheme I.2 Semantics of Scheme Part II: A catalog of Scheme II.0 Notational conventions II.1 Special forms II.2 Booleans II.3 Equivalence predicates II.4 Pairs and lists II.5 Symbols II.6 Numbers (part 1) II.7 Numbers (part 2) II.8 Strings II.9 Vectors II.10 The object table II.11 Procedures II.12 Ports II.13 Input II.14 Output References Index (not yet ready) Acknowledgements This report is primarily the work of a group of people who met at Brandeis University for two days in October 1984. The participants at that workshop were Hal Abelson (MIT), Norman Adams (Yale), David Bartley (Texas Instruments), Gary Brooks, (Texas Instruments, Indiana University), William Clinger (Indiana University), Dan Friedman (Indiana University), Robert Halstead (MIT), Chris Hanson (MIT), Chris Haynes (Indiana University), Eugene Kohlbecker (Indiana University), Don Oxley (Texas Instruments), Jonathan Rees (MIT), Bill Rozas (MIT), Gerald Sussman (MIT), and Mitchell Wand (Indiana University, Brandeis). Kent Pitman (MIT) made valuable contributions to the agenda for the workshop but was unable to attend the sessions. We would like also to thank the following people for their comments and criticisms in the months following the workshop: Kent Dybvig, Andy Freeman, Yekta Gursel, Paul Hudak, Chris Lindblad, Guy Steele Jr. We thank Dan Friedman and Chris Haynes for permission to use text from the Scheme 311 Version 4 reference manual. We acknowledge the influence of manuals for MIT Scheme, T, Scheme 84, and Common Lisp. Part I: Introduction to Scheme I.0 Brief history of Scheme Scheme is a programming language invented by Guy Lewis Steele Jr and Gerald Jay Sussman. It is a statically scoped dialect of Lisp with an exceptionally clear and simple semantics. The first description of Scheme was written in 1975 [\ref]. A Revised Report [\ref] appeared in 1978, which described the evolution of the language as its MIT implementation was upgraded to support an innovative compiler [\ref]. Three distinct projects began in 1981 and 1982 to use variants of Scheme for courses at MIT, Yale, and Indiana University [\ref, ref, ref]. An excellent introductory computer science textbook using Scheme was published in 1984 [\ref]. As might be expected of a language used primarily for education and research, Scheme has always evolved rapidly. This was no problem when Scheme was used only within MIT, but as Scheme became more widespread local subdialects began to diverge until students and researchers occasionally found it difficult to understand code written at other sites. Fifteen representatives of the major implementations of Scheme therefore met in October 1984 to work toward a better and more widely accepted standard for Scheme. This paper reports their unanimous recommendations augmented by committee work in the areas of input/output and arithmetic. 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 as much to do with porting ideas as with porting code. It is appropriate therefore that Scheme is much smaller, is less pervasively specified, and will evolve faster than Common Lisp. I.1 Syntax Formal definitions of the lexical and context-free syntaxes of Scheme will be added as appendices. Identifiers Most identifiers allowed by other programming languages are also acceptable to Scheme. The precise rules for forming identifiers vary among implementations of Scheme, but in all implementations a sequence of characters that contains no special characters and begins with a character that cannot begin a number is an identifier. There may be other identifiers as well, and in particular the following are identifiers: + - 1+ -1+ It is guaranteed that the following characters cannot begin a number, so identifiers other than the four listed above should begin with one of: a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ! $ % & * / : < = > ? ~ Subsequent characters of the identifier should be drawn from: a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ! $ % & * / : < = > ? ^ _ . ~ The case in which the letters of an identifier are typed is immaterial. For example, Foo is the same identifier as FOO. The following characters are special, and should never be used in an identifier: ) ( ] [ } { " ; blank Scheme deliberately does not specify whether the following characters can be used in identifiers: # ' ` , @ \ | Rationale: Some implementations might want to use backwhack ( \) and vertical bar ( | ) as in Common Lisp. As for the others there are two schools of thought. One school argues that disallowing special characters in identifiers allows the computer to catch more typing errors. The other school agrees only for special characters that come in pairs, because errors that involve only the unpaired special characters are easy to see. Numbers For a description of the notations used for numbers, see section II.7. A concise summary of the notations used to write numbers will be added to this section, and a formal description will be part of the appendix on lexical syntax. Comments A semicolon indicates the start of a comment. The comment continues to the end of the line on which the semicolon appears. Comments are invisible to Scheme, but the end of the line is visible as whitespace. This prevents a comment from appearing in the middle of an identifier or number. Comments may appear inside a string that extends over more than one line, but it is probably a bad idea to write such strings because their contents are implementation-dependent. Other notations Left and right parentheses are used for grouping and to notate lists as described in section II.4. Left and right square brackets and curly braces are not used in Scheme right now but are reserved for unspecified future uses. The quote (') and backquote (`) characters are used to indicate constant or almost-constant data as described in section II.1. The comma is used together with the backquote, and the atsign (@) is used together with the comma. The doublequote character is used to notate strings as described in section II.8. The sharp sign (octathorp) is used for a variety of purposes depending on the character that follows it. A sharp sign followed by a left parenthesis signals the beginning of a vector, as described in section II.9. A sharp sign followed by an exclamation point is used to notate one of the special values #!true, #!false, and #!null. A sharp sign followed by a backslash is used to notate characters as described in section II.8. A sharp sign followed by any of a number of letters is used to notate numbers as described in section II.7. Context free grammar for Scheme The following grammar is ambiguous because a looks like a . Some implementations resolve the ambiguity by reserving the identifiers that serve as keywords of special forms, while other implementations allow the keyword meaning of an identifier to be shadowed by lexical bindings. ::= | | | ::= | #!true | #!false | #!null | (quote ) | ' ::= ( ...) ::= ( ) ::= | stands for any written representation of a Scheme object, as described in the sections that follow. and have already been described informally. stands for one of the special forms whose syntax is described in section II.1. For uniformity the other kinds of expressions are also described in that section as though they were special forms. I.2 Semantics The formal semantics of Scheme will be the subject of an appendix to be added. The detailed informal semantics of Scheme is the subject of Part II. This section gives a quick review of Scheme's major characteristics. Scheme is a statically scoped programming language, meaning that each use of an identifier is associated with a lexically apparent binding of that identifier. In this respect Scheme is like Algol 60, Pascal, and C but unlike dynamically scoped languages such as APL and traditional Lisp. Scheme has latent as opposed to manifest types, which means that types are associated with values (also called objects) rather than with variables. (Some authors refer to languages with latent types as weakly typed or dynamically typed languages.) Other languages with latent types are APL, Snobol, and other dialects of Lisp. Languages with manifest types (sometimes referred to as strongly typed or statically typed languages) include Algol 60, Pascal, and C. All objects created in the course of a Scheme computation, including all procedures and variables, have unlimited extent. This means in effect that no Scheme object is ever destroyed. The reason that implementations of Scheme do not (usually!) run out of storage is that they are permitted to reclaim the storage occupied by an object if they can prove mathematically that the object cannot possibly matter to any future computation. Scheme is a practical programming language only because there exist fairly efficient "garbage collection" algorithms for performing these proofs. Other languages in which most objects have unlimited extent include APL and Common Lisp. Scheme procedures are objects in their own right. Procedures can be created dynamically, stored in data structures, returned as results of procedures, and so on. Other languages with these properties include Common Lisp, ML, and KRC. Arguments to Scheme procedures are always passed by value, which means that the actual argument expressions are evaluated before the procedure actually gains control, whether the procedure needs the result of the evaluation or not. ML, C, and APL are three good examples of other languages that always pass arguments by value. KRC is a good example of a language that passes arguments by name, so that an argument expression is evaluated only if its value is needed by the procedure. Part II: A catalog of Scheme II.0 Notational conventions This part of the report is a catalog of the special forms and procedures that make up Scheme. The special forms are described in section II.1, and the procedures are described in the following sections. Each section is organized into entries, with one entry (usually) for each special form or procedure. Each entry begins with a header line that includes the name of the special form or procedure in bold face type within a template for the special form or a call to the procedure. The names of the arguments to a procedure are italicized, as are the syntactic components of a special form. A notation such as expr ... indicates zero or more occurrences of expr, and so expr1 expr2 ... indicates at least one expr. At the right of the header line one of the following categories will appear: special form constant variable procedure essential special form essential constant essential variable essential procedure A special form is a syntactic class of expressions, usually identified by a keyword. A constant is something that is lexically recognizable as a constant. A variable is an identifier that is bound to a location in which values (also called objects) can be stored. Those variables that initially hold procedure values are identified as procedures. Implementations are free to omit features of Scheme or to add extensions, provided the extensions are not in conflict with the language reported here. It is guaranteed, however, that every implementation of Scheme will support the essential special forms, constants, variables, and procedures. Any Scheme value can be used as a boolean expression for the purposes of a conditional test. As explained in section II.2, most values count as true, but a few -- notably #!false -- count as false. This manual uses the word "true" to refer to any Scheme value that counts as true in a conditional expression, and the word "false" to refer to any Scheme value that counts as false. II.1. Special forms Identifiers have two uses within Scheme programs. When an identifier appears within a quoted constant (see quote), it is being used as data as described in the section on symbols. Otherwise it is being used as a name. There are two kinds of things that an identifier can name in Scheme: special forms and variables. A special form is a syntactic class of expressions, and an identifier that names a special form is called the keyword of that special form. A variable, on the other hand, is a location where a value can be stored. An identifier that names a variable is said to be bound to that location. The set of all such bindings in effect at some point in a program is known as the environment in effect at that point. Certain special forms are used to allocate storage for new variables and to bind identifiers to those new variables. The most fundamental of these binding constructs is the lambda special form, because all other binding constructs can be explained in terms of lambda expressions. The other binding constructs are the let, let*, letrec, internal definition (see define), rec, define!, defrec!, and do special forms. Like Algol or Pascal, and unlike most other dialects of Lisp except for Common Lisp, Scheme is a statically scoped language with block structure. To each place where an identifier is bound in a program there corresponds a region of the program within which the binding is effective. The region varies according to the binding construct that establishes the binding; if the binding is established by a lambda expression, for example, then the region is the entire lambda expression. Every use of an identifier in a variable reference or assignment refers to the binding of the identifier that set up the innermost of the regions containing the use. If there is no binding of the identifier whose region contains the use, then the use refers to the binding for the identifier that was in effect when Scheme started up, if any; if there is no binding for the identifier, it is said to be unbound. variable essential special form An expression consisting of an identifier that is not the keyword of a special form is a variable reference. The value obtained for the variable reference is the value stored in the location to which variable is bound. An error is signalled if variable is unbound. (procedure arg1 ...) essential special form A list of expressions whose first element is not the keyword of a special form indicates a procedure call. The procedure and argument expressions are evaluated and the procedure is passed the values of the argument expressions. In constrast to other dialects of Lisp the order of evaluation is not specified, and the procedure expression and the argument expressions are always evaluated in exactly the same way. (+ 3 4) --> 7 ((if #!false + *) 3 4) --> 12 (quote constant) essential special form 'constant essential special form Evaluates to constant. This notation is used to include literal constants in Scheme code. (quote a) --> a (quote #(a b c)) --> #(a b c) (quote (+ 1 2)) --> (+ 1 2) (quote constant) may be abbreviated as 'constant. The two notations are equivalent in all respects. 'a --> a '#(a b c) --> #(a b c) '(+ 1 2) --> (+ 1 2) '(quote a) --> (quote a) ''a --> (quote a) Numbers, strings, and the constants #!true, #!false, and #!null need not be quoted. '"abc" --> "abc" "abc" --> "abc" '145932 --> 145932 145932 --> 145932 '#!true --> #!true #!true --> #!true (lambda (var1 ...) expr) essential special form Each var must be an identifier. The lambda expression evaluates to a procedure with formal argument list (var1 ...) and procedure body expr. The environment in effect when the lambda expression was evaluated is remembered as part of the procedure. When the procedure is later called with some actual arguments, the environment in which the lambda expression was evaluated will be extended by binding the identifiers in the formal argument list to fresh locations, the corresponding actual argument values will be stored in those locations, and expr will then be evaluated in the extended environment. The result of expr will be returned as the result of the procedure call. (lambda (x) (+ x x)) --> # ((lambda (x) (+ x x)) 4) --> 8 (define reverse-subtract (lambda (x y) (- y x))) --> reverse-subtract (reverse-subtract 7 10) --> 3 (define foo (let ((x 4)) (lambda (y) (+ x y)))) --> foo (foo 6) --> 10 (lambda (var1 ...) expr1 expr2 ...) essential special form Equivalent to (lambda (var1 ...) (begin expr1 expr2 ...)). (lambda var expr1 expr2 ...) essential special form Returns a procedure that when later called with some arguments will bind var to a fresh location, convert the sequence of actual arguments into a list, and store that list in the binding of var. ((lambda x x) 3 4 5 6) --> (3 4 5 6) One last variation on the formal argument list provides for a so-called rest argument. If a space/dot/space sequence precedes the last argument in the formal argument list, then the value stored in the binding of the last formal argument will be a list of the actual arguments left over after all the other actual arguments have been matched up against the formal arguments. ((lambda (x y . z) z) 3 4 5 6) --> (5 6) (if condition consequent alternative) essential special form (if condition consequent) essential special form First evaluates condition. If it yields a true value (see section II.2), then consequent is evaluated and its value is returned. Otherwise alternative is evaluated and its value is returned. If no alternative is specified, then the if expression is evaluated only for its effect, and the result of the expression is unspecified. (if (>? 3 2) 'yes 'no) --> yes (if (>? 2 3) 'yes 'no) --> no (if (>? 3 2) (- 3 2) (+ 3 2)) --> 1 (cond clause1 clause2 ...) essential special form Each clause must be a list of one or more expressions. The first expression in each clause is a boolean expression that serves as the guard for the clause. The guards are evaluated in order until one of them evaluates to a true value (see section II.2). When a guard evaluates true, then the remaining expressions in its clause are evaluated in order, and the result of the last expression in the selected clause is returned as the result of the entire expression. If the selected clause contains only the guard, then the value of the guard is returned as the result. If all tests evaluate to false values, then the result of the conditional expression is unspecified. (cond ((>? 3 2) 'greater) (( greater The special keyword else may be used as a guard to obtain the effect of an otherwise clause. (cond ((>? 3 3) 'greater) (( equal The above forms for the clauses are essential. Some implementations support yet another form of clause such that (cond (form1 => form2) ...) is equivalent to (let ((form1_result form1) (thunk2 (lambda () form2)) (thunk3 (lambda () (cond ...)))) (if form1_result ((thunk2) form1_result) (thunk3))) (case expr clause1 clause2 ...) special form Each clause is a list whose first element is a selector followed by one or more expressions. Each selector should be a list of values. The selectors are not evaluated. Instead expr is evaluated and its result is compared against successive selectors using the memv procedure until a match is found. Then the expressions in the selected clause are evaluated from left to right and the result of the last expression in the clause is returned as the result of the case expression. If no selector matches then the result of the case expression is unspecified. (case (* 2 3) ((2 3 5 7) 'prime) ((1 4 6 8 9) 'composite)) --> composite (case (car '(c d)) ((a) 'a) ((b) 'b)) --> unspecified The special keyword else may be used as a selector to obtain the effect of an otherwise clause. (case (car '(c d)) ((a e i o u) 'vowel) ((y) 'y) (else 'consonant)) --> consonant (and expr1 ...) special form Evaluates the exprs from left to right, returning false as soon as one evaluates to a false value (see section II.2). Any remaining expressions are not evaluated. If all the expressions evaluate to true values, the value of the last expression is returned. (and (=? 2 2) (>? 2 1)) --> #!true (and (=? 2 2) ( #!false (and 1 2 'c '(f g)) --> (f g) (or expr1 ...) special form Evaluates the exprs from left to right, returning the value of the first expr that evaluates to a true value (see section II.2). Any remaining expressions are not evaluated. If all expressions evaluate to false values, false is returned. (or (=? 2 2) (>? 2 1)) --> #!true (or (=? 2 2) ( #!true (or #!false #!false #!false) --> #!false (or (memq 'b '(a b c)) (/ 3 0)) --> (b c) (let ((var1 form1) ...) expr1 expr2 ...) essential special form Evaluates the forms in the current environment (in some unspecified order), binds the vars to fresh locations holding the results, and then evaluates the exprs in the extended environment, returning the value of the last one. Each binding of a var has expr1 expr2 ... as its region. (let ((x 2) (y 3)) (* x y)) --> 6 (let ((x 2) (y 3)) (let ((foo (lambda (z) (+ x y z))) (x 7)) (foo 4))) --> 9 let and letrec give Scheme a block structure. The difference between let and letrec is that in a let the forms are not within the regions of the vars being bound. See letrec. Some implementations of Scheme permit a "named let" syntax in which (let name ((var1 form1) ...) expr1 expr2 ...) is equivalent to ((rec name (lambda (id1 ...) expr1 expr2 ...)) form1 ...) (let* ((var1 form1) ...) expr1 expr2 ...) special form Similar to let, but the bindings are performed sequentially from left to right and the region of a binding indicated by (var form) is that part of the let* expression to the right of the binding. Thus the second binding is done in an environment in which the first binding is visible, and so on. (letrec ((var1 form1) ...) expr1 expr2 ...)essential special form Binds the vars to fresh locations holding undefined values, evaluates the forms in the resulting environment (in some unspecified order), assigns to each var the result of the corresponding form, evaluates the exprs sequentially in the resulting environment, and returns the value of the last expr. Each binding of a var has the entire letrec expression as its region, making it possible to define mutually recursive procedures. See let. (letrec ((x 2) (y 3)) (letrec ((foo (lambda (z) (+ x y z))) (x 7)) (foo 4))) --> 14 (letrec ((even? (lambda (n) (if (zero? n) #!true (odd? (-1+ n))))) (odd? (lambda (n) (if (zero? n) #!false (even? (-1+ n)))))) (even? 88)) --> #!true One restriction on letrec is very important: it must be possible to evaluate each form without referring to the value of a var. If this restriction is violated, then the effect is undefined, and an error may be signalled during evaluation of the forms. The restriction is necessary because Scheme uses call by value rather than call by name. In the most common uses of letrec, all the forms are lambda expressions and the restriction is satisfied automatically. (rec var expr) special form Equivalent to (letrec ((var expr)) var). rec is useful for defining self-recursive procedures. (named-lambda (name var1 ...) expr ...) special form Equivalent to (rec name (lambda (var1 ...) expr ...)) Rationale: Some implementatations may find it easier to provide good debugging information when named-lambda is used instead of rec. (define var expr) essential special form When typed at top level (so that it is not nested within any other expression), this form has essentially the same effect as (set! var expr) if var is bound. If var is not bound, however, then the define form will bind var before performing the assignment, whereas it would be an error to perform a set! on an unbound identifier. The value returned by a define form is not specified. (define add3 (lambda (x) (+ x 3))) --> unspecified (add3 3) --> 6 (define first car) --> unspecified (first '(1 2)) --> 1 The semantics just described is essential. Some implementations also allow define expressions to appear at the beginning of the body of a lambda, named-lambda, let, let*, or letrec expression. Such expressions are known as internal definitions as opposed to the top level definitions described above. The variable defined by an internal definition is local to the body of the lambda, named-lambda, let, let*, or letrec expression. That is, var is bound rather than assigned, and the region set up by the binding is the entire body of the lambda, named-lambda, let, let*, or letrec expression. For example, (let ((x 5)) (define foo (lambda (y) (bar x y))) (define bar (lambda (a b) (+ (* a b) a))) (foo (+ x 3))) --> 45 Internal definitions can always be converted into an equivalent letrec expression. For example, the let expression in the above example is equivalent to (let ((x 5)) (letrec ((foo (lambda (y) (bar x y))) (bar (lambda (a b) (+ (* a b) a)))) (foo (+ x 3)))) (define (var0 var1 ...) expr1 expr2 ...) special form (define (form var1 ...) expr1 expr2 ...) special form The first form, where var0 is an identifier, is equivalent to (define var0 (rec var0 (lambda (var1 ...) expr1 expr2 ...))). The second form, where form is a list, is sometimes convenient for defining a procedure that returns another procedure as its result. It is equivalent to (define form (lambda (var1 ...) expr1 expr2 ...)). (define! var expr) special form If var is bound, then the define! form is equivalent to the corresponding set!. If var is unbound, however, define! binds var in the global environment before performing the assignment. Rationale: This makes sense only for implementations with a distinguished global environment. (defrec! var expr) special form Equivalent to (define! var (rec var expr)). (set! var expr) essential special form Stores the value of expr in the location to which var is bound. expr is evaluated but var is not. The result of the set! expression is unspecified. (set! x 4) --> unspecified (1+ x) --> 5 Rationale: set! expressions are Scheme's assignment statements. Assignment statements are seldom used in good Scheme code. Their major uses are to initialize variables accessed interactively and to construct objects with local state. (begin expr1 expr2 ...) essential special form Evaluates the exprs sequentially from left to right and returns the value of the last expr. Used to sequence side effects such as input and output. (begin (set! x 5) (1+ x)) --> 6 Also (begin (display "4 plus 1 equals ") (display (1+ 4))) prints 4 plus 1 equals 5 A number of special forms such as lambda and letrec implicitly treat their bodies as begin expressions. (sequence expr1 expr2 ...) special form sequence is synonymous with begin. Rationale: sequence was used in the Abelson and Sussman text, but it should not be used in new code. (do ((var1 init1 step1) ...) special form (test expr1 ...) stmt1 ...) The do special form is an extremely general albeit complex iteration macro. Each var must be an identifier and each init and step must be expressions. The init expressions are evaluated (in some unspecified order), the vars are bound to fresh locations, the results of the init expressions are stored in the bindings of the vars, and then the iteration phase begins. Each iteration begins by evaluating test; if the result is false (see section II.2), then the stmts are evaluated in order for effect, the steps are evaluated (in some unspecified order), the results of the step expressions are stored in the bindings of the vars, and the next iteration begins. If test evaluates true, then the exprs are evaluated from left to right and the value of the last expr is returned as the value of the do expression. If no exprs are present, then the value of the do expression is unspecified. The region set up by the binding of a var consists of the entire do expression except for the inits. A step may be omitted, in which case the corresponding var is not updated. When the step is omitted the init may be omitted as well, in which case the initial value is not specified. (do ((vec (make-vector 5)) (i 0 (1+ i))) ((=? i 5) vec) (vector-set! vec i i)) --> #(0 1 2 3 4) (let ((x '(1 3 5 7 9))) (do ((x x (cdr x)) (sum 0 (+ sum (car x)))) ((null? x) sum))) --> 25 The do special form is essentially the same as the do macro in Common Lisp. The main difference is that in Scheme the identifier return is not bound; programmers that want to bind return as in Common Lisp must do so explicitly (see call-with-current-continuation). `pattern macro special form The backquote special form is useful for constructing a list structure when most but not all of the desired structure is known in advance. If no commas appear within the pattern, the result of evaluating `pattern is equivalent (in the sense of equal?) to the result of evaluating 'pattern. If a comma appears within the pattern, however, the expression following the comma is evaluated and its result is inserted into the structure instead of the comma and the expression. If a comma appears followed immediately by an at-sign (@), then the following expression must evaluate to a list; the opening and closing parentheses of the list are then "stripped away" and the elements of the list are inserted in place of the comma/at-sign/expression sequence. `(a ,(+ 1 2) ,@(mapcar 1+ '(4 5 6)) b) --> (a 3 5 6 7 b) `(((foo ,(- 10 3)) ,@(cdr '(c)) cons)) --> (((foo 7) cons)) Scheme does not have any standard facility for defining new special forms. Rationale: The ability to define new special forms creates numerous problems. All current implementations of Scheme have macro facilities that solve those problems to one degree or another, but the solutions are quite different and it isn't clear at this time which solution is best, or indeed whether any of the solutions are truly adequate. Rather than standardize, we are encouraging implementations to continue to experiment with different solutions. The main problems with traditional macros are: They must be defined to the system before any code using them is loaded; this is a common source of obscure bugs. They are usually global; macros can be made to follow lexical scope rules as in Common Lisp's macrolet, but many people find the resulting scope rules confusing. Unless they are written very carefully, macros are vulnerable to inadvertant capture of free variables; to get around this, for example, macros may have to generate code in which procedure values appear as quoted constants. There is a similar problem with keywords if the keywords of special forms are not reserved. If keywords are reserved, then either macros introduce new reserved words, invalidating old code, or else special forms defined by the programmer do not have the same status as special forms defined by the system. II.2. Booleans The standard boolean objects for truth and falsity are written as #!true and #!false. What really matters, though, are the objects that the Scheme conditional expressions (if, cond, and, or, do) will treat as though they were true or false. The phrase "a true value" (or sometimes just "true") means any object treated as true by the conditional expressions, and the phrase "a false value" (or "false") means any object treated as false by the conditional expressions. Of all the standard Scheme values, only #!false and the empty list count as false in conditional expressions. #!true, pairs (and therefore lists), symbols, numbers, strings, vectors, and procedures all count as true. The empty list counts as false for historical reasons only, and programs should not rely on this because future versions of Scheme will probably do away with this nonsense. Programmers accustomed to other dialects of Lisp should beware that Scheme has already done away with the nonsense that identifies the empty list with the symbol nil. #!false essential constant #!false is the boolean value for falsity. The #!false object is self-evaluating. That is, it does not need to be quoted in programs. '#!false --> #!false #!false --> #!false #!true essential constant #!true is the boolean value for truth. The #!true object is self-evaluating, and does not need to be quoted in programs. (not obj) essential procedure Returns #!true if obj is false and returns #!false otherwise. nil variable t variable As a crutch for programmers accustomed to other dialects of Lisp, some implementations provide variables nil and t whose initial values are #!false and #!true respectively. These variables should not be used in new code. II.3. Equivalence predicates A predicate is a procedure that always returns #!true or #!false. Of the equivalence predicates described in this section, eq? is the most discriminating while equal? is the most liberal. eqv? is very slightly less discriminating than eq?. (eq? obj1 obj2) essential procedure Returns #!true if obj1 is identical in all respects to obj2, otherwise returns #!false. If there is any way at all that a  Received: from CSNET-SH by MIT-MC.ARPA; 19 MAR 85 07:55:45 EST To: scheme@MIT-MC.ARPA cc: willc%indiana.csnet@CSNET-SH.ARPA Subject: Message a011267 -- long message to scheme@mit-mc Reply-to: cic@CSNET-SH.ARPA Date: 19 Mar 85 07:45:58 EST (Tue) From: "Charlotte D. Mooers" This message was rejected by mit-mc for the following reason: 'Message is too large to mail; use FTP.' Since FTP (file transfer protocol) is not possible between indiana and mit-mc, I have split up the message into three sections of 1000, 1000 and 695 lines each, which I will send separately. In the future, please agree on a limit to the length of file that you will transmit by mail to mit-mc. ---Charlotte Mooers CSNET Staff  Received: from csnet-relay by MIT-MC.ARPA; 18 MAR 85 23:54:51 EST Received: from indiana by csnet-relay.csnet id ab04361; 18 Mar 85 23:38 EST Date: Mon, 18 Mar 85 16:46:58 est From: Will Clinger Received: by iuvax.UUCP; id AA08003; Mon, 18 Mar 85 16:46:58 est To: scheme@mit-mc.ARPA Subject: Re: I/O proposal We thank everyone for their comments on the I/O proposal. Here are our responses. >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. Agreed. Any dissent? ---------------------------------------------------------------- >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. >-- I oppose the term "stream" and thus the names STREAM?, CURRENT-INPUT- >STREAM, etc., for the reasons Gerry gave. We use "port". Good point, so the names will change as follows: old name new name stream? input-port? output-port? current-input-stream current-input-port current-output-stream current-output-port close-input-stream close-input-port close-output-stream close-output-port ---------------------------------------------------------------- >-- What happened to the names PRIN1 and PRINC? Are your WRITE and DISPLAY >meant to be equivalent, respectively, to PRIN1 and PRINC? As I understand >page 382 of the Common LISP (CL) book, your WRITE is not incompatible with >CL's. Likewise, CL doesn't seem to have a DISPLAY. However, I am bothered >by my intuition that DISPLAY should be a terminal-oriented operation. > > Bottom line: I vote to go with WRITE and DISPLAY, but the names PRIN1 >and PRINC should be reserved as well (as optional aliases). The PRINT >operation should also be optional, with the CL-compatible meaning we agreed >on at the workshop. Yes, WRITE and DISPLAY correspond to Common Lisp's PRIN1 and PRINC. For that matter they correspond to T's PRINT and DISPLAY, and to Scheme-84's DISPLAY and PRINT. Common Lisp's WRITE takes a stream as a keyword argument whereas Scheme's WRITE takes a port as an optional argument. If that incompatibility doesn't bother you then it certainly doesn't bother us. PRINC and PRINT are used in the Abelson and Sussman book, but we don't think they deserve or need formal status. ---------------------------------------------------------------- >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. Scheme doesn't have anything comparable to Common Lisp's READ-FROM-STRING, WRITE-TO-STRING, WITH-INPUT-FROM-STRING, and WITH-OUTPUT-TO-STRING. When we get around to adding those capabilities, we will want to package the strings up to look like ordinary I/O ports that can be passed as the optional argument to READ and WRITE. It appears that the designers of Common Lisp didn't quite figure out how they wanted to do it either. ---------------------------------------------------------------- >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 ? We agree that this is somewhat inconsistent, but there are difficulties involved with specifying the correct semantics for WITH-INPUT-FROM-FILE and WITH-OUTPUT-TO-FILE, as well as difficulties involved with implementing them correctly, so for the time being... WITH-INPUT-FROM-FILE and WITH-OUTPUT-TO-FILE are the only ways defined in the proposal to change the default input or output ports. Implementations may feel that the default ports should always be the keyboard and screen, or they may prefer alternative ways to change the default ports. The main reason for making WITH-INPUT-FROM-FILE and WITH-OUTPUT-TO-FILE optional rather than essential, however, is that we are not certain of the best way for them to interact with escape procedures, and it seems a good idea to keep uncertainties like that out of the essential language until we understand them better. ---------------------------------------------------------------- >-- DISPLAY-CHAR seems to be a redundant, restricted form of DISPLAY. Since >DISPLAY is essential, DISPLAY-CHAR should be dropped. At the workshop it was emphasized that characters may overlap with numbers, strings, or what have you. If characters are just numbers, then (DISPLAY #\SPACE) will print something like "32" whereas (WRITE-CHAR #\SPACE) will print " " (without the double quotes, of course). >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. WRITE-CHAR it is. ---------------------------------------------------------------- >I don't see any reason for LOAD and CURRENT-INPUT-STREAM to interact, >and I think it is much better for them not to. If they don't interact >you have a much better invariants on each, e.g.: doing LOAD on a file is >the same as doing LOAD on a file consisting of the string "(begin " >appended to the file appended to the string ")". We agree entirely, and are flushing the sentence in question. ---------------------------------------------------------------- >-- The name LISTEN? also is slightly unintuitive to me; how about INPUT?, >or MORE-INPUT?, or something with "peek" in it. LISTEN? is named after the LISTEN predicate in Common Lisp. "Peek" calls to mind MacLisp's TYIPEEK, which is unrelated. We're willing to change the name, and the following are the leading candidates: CHAR-READY? INPUT-READY? Please send your votes to brooks@indiana before 25 March. ---------------------------------------------------------------- >-- Does READ-CHAR wait for interactive input or return an eof token if >nothing has been buffered up from the keyboard (or comm device, etc)? It waits. ---------------------------------------------------------------- >-- With my Pascal (and English) background, I can't help but think of >features named with "with", like WITH-INPUT-FROM-FILE, as syntax, not >procedures. [Did you notice how I began the previous sentence?] I would >prefer a special form: (WITH-INPUT-FROM-FILE exp ...). This is >more consistent with CL's macro WITH-INPUT-FROM-STRING. The same goes for >WITH-OUTPUT-FROM-FILE. We prefer procedures to special forms, and we followed MIT's names here. It is hard to come up with good names for these procedures. Thanks again to all who are commenting on the proposals. Gary Brooks and Will Clinger  Received: from mitre-bedford by MIT-MC.ARPA; 18 MAR 85 18:38:27 EST Date: 18 Mar 1985 18:36:50-EST From: linus!ramsdell@Mitre-Bedford Received: by linus.UUCP (4.12/4.7) id AA07064; Mon, 18 Mar 85 15:17:32 est Date: Mon, 18 Mar 85 15:17:32 est From: linus!ramsdell (John D. Ramsdell) Message-Id: <8503182017.AA07064@linus.UUCP> To: bccvax!scheme@mit-mc.arpa Subject: Pointer-less stack allocated arrays. I recently visited Harvard and had an interesting conversation with Stavros Macrakis. He is an Ada proponent with considerable experience with Lisp, in particular, MacLisp. He was very active in the Macsyma project and gained much knowledge about MacLisp's implementation. I brought up the subject of extensible languages in reference to Richard Schooler's masters thesis called "Partial Evaluation as a means of Language Extensibility", MIT/LCS/TR-324, August 1984. The thesis suggests that Scheme makes a good intermediate language; very suitable for partial evaluation. Stavros immediately rejected the notion that Scheme makes a good intermediate language because its model of data is too general. For example, consider the task of taking the dot product of two vectors represented by two arrays of floating point numbers. Since arrays can be returned from the function in which they are created, they must be allocated from the heap. Since garbage collection can change the origin of the array, all references to elements in the array must be accompanied with a reference to the array header. Thus the compiled code for Scheme can never be as good as that produced by an Ada compiler said Starvos. Many years ago, I asked Jonathan Rees if the T compiler could place arrays on the stack when appropriate and eliminate the unneccessary indirection in numerical arrays by replacing pointers to floating point numbers with the actual numbers as the array elements. He claimed compiler declarations would make this possible, but had many other more pressing issue to worry about in regards to the T compiler. I am wondering if there is interest in using ASSERT to allow the elimination of many pointers in some programs. John  Received: from csnet-relay by MIT-MC.ARPA; 18 MAR 85 15:08:58 EST Received: from indiana by csnet-relay.csnet id a001575; 18 Mar 85 14:38 EST Date: Mon, 18 Mar 85 10:48:24 est From: Will Clinger Received: by iuvax.UUCP; id AA03030; Mon, 18 Mar 85 10:48:24 est To: scheme@mit-mc.ARPA Subject: Arithmetic overflow Kent Pitman points out: >Please be careful when writing your compilers not to copy the Maclisp bug >where: > >(DEFUN F (X Y) (DECLARE (FIXNUM X Y)) (PLUS X Y)) > >turns into a single-instruction addition, ignoring overflow. Gerry's proposal >does not specify much of any way to get code that can do that. It was an >unfortunate mistake since when X and Y are large, the addition can overflow >and bad values can get returned (something GJS's proposal frowns on). I agree whole-heartedly. Like Gerry's proposal, the ASSERT procedure does not give the programmer the ability to specify that overflow is to be ignored, though it can be used to assert that a result is within a certain range. My last fibonacci example was perhaps too subtle. In computing (fib 40), the arguments to + are always less than 100000000 (the constant appearing in the fixnum? procedure), but the result is greater than 100000000. A compiler that assumes that the result of an addition is never greater than the operands is simply buggy. Peace, Will Clinger  Received: from MIT-OZ by MIT-MC via Chaosnet; 18 MAR 85 14:56:49 EST Date: Mon 18 Mar 85 14:01:07-EST From: Gerald Jay Sussman Subject: Re: assertions are better than types To: willc%indiana.csnet@CSNET-RELAY.ARPA cc: scheme@MIT-MC.ARPA In-Reply-To: Message from "Will Clinger " of Sun 17 Mar 85 23:42:56-EST Hi. Before you jump, I just want to clarify a few points about what I meant by type-restricted operators -- I know I didn't say this in the draft I sent you, but I didn't realize the scope of possible confusion. I rspond pointwise: Willc: Speaking for myself, I would not want to clutter my code with all these type-restricted operators as I write the code, because it would make the code less readable. (I guess readability is more important to my debugging processes than are type checks.) I would be willing to add type assertions when the time came to make the code run fast. Hence for me at least, the type restrictions would be more important as advice to the compiler than as debugging aids. GJS: I agree, usually we would not write the ugly kind of code which is illustrated below: (define (tak x y z) (if (not (real x)))) (put-op! (raise-rational-unary-operation sqrt) sqrt rational) (put-op! (raise-rational-unary-operation sin) sin rational) (put-op! (raise-rational-unary-operation cos) cos rational) (put-op! (raise-rational-unary-operation tan) tan rational) (put-op! (raise-rational-unary-operation real-part) real-part rational) (put-op! (raise-rational-unary-operation imag-part) imag-part rational) (put-op! (raise-rational-unary-operation magnitude) magnitude rational) (put-op! (raise-rational-unary-operation angle) angle rational) -------  Received: from MIT-OZ by MIT-MC via Chaosnet; 18 MAR 85 14:20:44 EST Date: Mon 18 Mar 85 14:09:28-EST From: Gerald Jay Sussman Subject: Re: I/O proposal To: dyb%unc.csnet@CSNET-RELAY.ARPA cc: scheme@MIT-MC.ARPA In-Reply-To: Message from "Kent Dybvig " of Mon 18 Mar 85 14:02:16-EST I think that syntax is not the right thing if we can avoid it. For example, one can pass a procedure WITH-OUTPUT-TO-FILE as an argument or return it as a value... We cannot do that with a syntactic form. Thus, unless there are overwhelming reasons for introducing syntax, we really should not do so. -------  Date: 18 March 1985 13:48-EST From: Jonathan A Rees Subject: I/O proposal To: dyb%unc.csnet @ CSNET-RELAY cc: SCHEME @ MIT-MC In-reply-to: Msg of Sat 16 Mar 85 22:12:45 est from Kent Dybvig Date: Sat, 16 Mar 85 22:12:45 est From: Kent Dybvig > I disagree -- proliferation of special forms is a communicable disease. Addition of new syntax where new syntax makes sense is never harmful. It's much more clumsy to use a function when what really makes sense is a proceedure. Ninety-nine times out of a hundred people are going to type: (with-output-to-file "fudmich" (lambda () ...)) Adding an extra level of complexity (not to mention an extra level of nesting). I would not argue strongly if you claimed that antispecialformism was a purely religious position (although I'd wonder, since it's so strongly held in some camps, such as the ones I belong to). One rational argument, however, might be not necessarily language simplicity, as you suggested, but rather, program analyzability. Any program-analyzing program or human must know about ALL special forms in order to do things such as free variable analysis and substitution; and there is much one can say about a program without having ANY knowledge of the values of free variables. So, generally speaking, special forms add to the complexity of program analysis, whereas procedures (i.e. bindings) do not, and therefore special forms are to be avoided. This argument does not necessarily apply to macros (since macro forms can be expanded), but since Essential Scheme is militant in not having a theory of macros, that doesn't help much. If we continue to use functions where syntax is appropriate, pretty soon someone will suggest that the reader syntax "#'exp" should mean "(lambda () exp)". Actually this is not so absurd. I have from time to time toyed with the idea of a reader syntax for (lambda () ...) in analogy to that for (quote ...) for this very reason - the presence of such a thing would often shoot down the conciseness arguments sometimes put forth in favor of special forms. Jonathan  Received: from csnet-relay by MIT-MC.ARPA; 18 MAR 85 13:25:10 EST Received: from unc by csnet-relay.csnet id ae00998; 18 Mar 85 13:22 EST Received: by unc (4.12/4.7) id AA18455; Sat, 16 Mar 85 22:12:45 est Date: Sat, 16 Mar 85 22:12:45 est From: Kent Dybvig Message-Id: <8503170312.AA18455@unc> To: scheme@mit-mc.ARPA Subject: Re: I/O proposal (This note should have come before the others, as it was referenced in one of them -- sorry.) > I disagree -- proliferation of special forms is a communicable disease. Addition of new syntax where new syntax makes sense is never harmful. It's much more clumsy to use a function when what really makes sense is a proceedure. Ninety-nine times out of a hundred people are going to type: (with-output-to-file "fudmich" (lambda () ...)) Adding an extra level of complexity (not to mention an extra level of nesting). If we continue to use functions where syntax is appropriate, pretty soon someone will suggest that the reader syntax "#'exp" should mean "(lambda () exp)". If keeping the language small is the goal, proliferation of functions is more harmful; the ``core'' set must include only special forms that cannot be implemented using syntactic- extension, plus all of the functions. I can preprocess a program on my system to take out the syntax before sending it to you to run it on your system, but you must have the functions I call (unless I send them all to you). Granted, with this particular special form, the standard does not give us enough information to write it as syntax; this is a shortcoming in the standard, not in syntactic-extension. ..Kent  Received: from SCRC-STONY-BROOK by MIT-MC via Chaosnet; 18 MAR 85 01:46:45 EST Received: from SCRC-RIO-DE-JANEIRO by SCRC-STONY-BROOK via CHAOS with CHAOS-MAIL id 198848; Mon 18-Mar-85 01:47:30-EST Date: Mon, 18 Mar 85 01:45 EST From: Kent M Pitman Subject: assertions are better than types To: willc%indiana.csnet@CSNET-RELAY.ARPA, scheme@MIT-MC.ARPA In-Reply-To: The message of 17 Mar 85 17:21-EST from Will Clinger Message-ID: <850318014558.4.KMP@RIO-DE-JANEIRO.SCRC.Symbolics.COM> I haven't had a chance to go over the recent burst of proposals in enough depth to comment, but will get to it. Let me mention, though, while I'm thinking about it, that with regard to: Date: Sun, 17 Mar 85 17:21:05 est From: Will Clinger ... Here is doubly recursive Fibonacci: ; Gerry's proposal (define (fib n) (if ( From: CPH%MIT-OZ@MIT-MC.ARPA To: Will Clinger Cc: scheme@MIT-MC.ARPA Subject: assertions are better than types In-reply-to: Msg of 17 Mar 1985 17:21-EST from Will Clinger Nice! I like it.  Received: from csnet-relay by MIT-MC.ARPA; 17 MAR 85 23:40:21 EST Received: from indiana by csnet-relay.csnet id aa08590; 17 Mar 85 23:36 EST Date: Sun, 17 Mar 85 17:21:05 est From: Will Clinger Received: by iuvax.UUCP; id AA03798; Sun, 17 Mar 85 17:21:05 est To: scheme@mit-mc.ARPA Subject: assertions are better than types Gerry's numbers proposal is the first attempt I've ever seen at doing numbers right, and I'm very excited about it. I've become concerned about the effectiveness of the type-restricted operators, however, and I'd like to offer an alternative proposal. First of all, let me state my understanding of Gerry's proposal, as I edited it for the Revised Revised Report. (I know it isn't written very well -- my concern was growing as I wrote it.) ---------------------------------------------------------------- (number op) procedure (complex op) procedure (real op) procedure (rational op) procedure (integer op) procedure These procedures take a numeric operator op and return a procedure that acts like op except that it is restricted to operate on numbers of the specified type and to return numbers of the specified type. The idea of these procedures is expressed by (define make-type-restrictor (lambda (type?) (lambda (op) (lambda args (if (every type? args) (let ((ans (apply op args))) (if (type? ans) ans (error "Bad result from type-restricted operator"))) (error "Bad argument to type-restricted operator")))))) (define number (make-type-restrictor number?)) (define complex (make-type-restrictor complex?)) (define real (make-type-restrictor real?)) (define rational (make-type-restrictor rational?)) (define integer (make-type-restrictor integer?)) where EVERY returns true if its first argument, a predicate, is true of every element of its second argument, a list. Some implementations may provide a mechanism whereby the programmer can request that these type restriction procedures be interpreted as assertions intended to make the code run faster rather than as requests for type checks. ((real sqrt) 5) --> 2.236067977 ((real sqrt) -1) --> error ((integer sqrt) 10) --> error The programmer who wrote the last example probably wanted to compute (TRUNCATE ((REAL SQRT) 10)). [Gerry's rationale would go here.] ---------------------------------------------------------------- My objections: 1. Speaking for myself, I would not want to clutter my code with all these type-restricted operators as I write the code, because it would make the code less readable. (I guess readability is more important to my debugging processes than are type checks.) I would be willing to add type assertions when the time came to make the code run fast. Hence for me at least, the type restrictions would be more important as advice to the compiler than as debugging aids. 2. The type restrictors require that all operands and the result be of the same type. This works reasonably well for numeric operators but is going to be awkward when the time comes to generalize it to other operators. How am I going to tell the compiler that the first argument to vector-ref is a vector of strings? 3. Statically typed languages are mistaken in associating types with variables, and Gerry is on the right track to associate type restrictions with operators, but I think most of us would agree that the right thing to do is to associate types with values. That leads to my proposal, which follows: ---------------------------------------------------------------- (assert property? obj) procedure PROPERTY? must be a total (everywhere defined) unary predicate with no side effects. Returns obj. It is an error if PROPERTY? is not true of obj, but implementations are not required to detect the error. Rationale: In interpreted code, assertions can be used to check types, loop invariants, and other assertions. In compiled code, calls to ASSERT have zero overhead but help the compiler to produce better code. The ASSERT procedure is a moby generalization of Common Lisp's special form called THE, and of MacLisp's FIXNUM-IDENTITY and FLONUM-IDENTITY. ---------------------------------------------------------------- Examples: Here is Gabriel's tak benchmark with type-restricted operators as in Gerry's proposal: (define (tak x y z) (if (not ( From: CPH%MIT-OZ@MIT-MC.ARPA To: Bill Rozas Cc: Kent Dybvig , scheme@MIT-MC.ARPA Subject: I/O proposal In-reply-to: Msg of 17 Mar 1985 11:37-EST from Bill Rozas Date: Sunday, 17 March 1985 11:37-EST From: Bill Rozas 1. Having a way to detect the end of file is needed, beyond that, both alternatives (eof? or *eof*) can be trivially implemented on top of the other. I don't feel strongly about either, but I tend to agree with Kent Dybvig since I view *eof* as more flexible. If the other option (eof?) is chosen, I would much rather have the name be eof-object? . I don't think that *EOF* is a good idea, for the following reason: if the I/O primitives are expected to return this object, they will have to return it, and if they are written in assembly or some other non-Scheme language it would require them to do a variable reference. Or, at the very least, to have some kind of Scheme wrapper that either passed them the eof object, or detected that they had returned some other object and substituted that one. I vastly prefer the idea of a small fixed set of end of file objects, and I wish that I had thought up the idea myself. However, I agree that EOF-OBJECT? is a much better name. If it is felt that there is a need for something like *EOF*, I would feel better about having a procedural binder, something like (WITH-EOF-OBJECT (LAMBDA () ...)) which would give the implementer more freedom. 2. I disagree that the programmer should not be able to change the current i/o streams. The debugging problem can be trivially fixed if the error system/debugger always installs the appropriate stream, and this can be accomplished easily by appropriate with-input-from-stream and with-output-from-stream. This has been my experience -- we have had this stuff running for quite some time without problems.  Received: from MIT-OZ by MIT-MC via Chaosnet; 17 MAR 85 11:37:49 EST Date: 17 Mar 1985 11:37 EST (Sun) Message-ID: From: Bill Rozas To: Kent Dybvig Cc: scheme@MIT-MC.ARPA Subject: I/O proposal In-reply-to: Msg of 16 Mar 1985 07:34-EST from Kent Dybvig Just a few comments: 1. Having a way to detect the end of file is needed, beyond that, both alternatives (eof? or *eof*) can be trivially implemented on top of the other. I don't feel strongly about either, but I tend to agree with Kent Dybvig since I view *eof* as more flexible. If the other option (eof?) is chosen, I would much rather have the name be eof-object? . 2. I disagree that the programmer should not be able to change the current i/o streams. The debugging problem can be trivially fixed if the error system/debugger always installs the appropriate stream, and this can be accomplished easily by appropriate with-input-from-stream and with-output-from-stream. Note that allowing the programmer to change them allows for a cheap implementation of trasncript-on and transcript-off if the stream objects are appropriately powerful (which they probably want to be). There is good reason for making them functions rather thatn variables. If they are variables, the implementation cannot easily cash the internal components of a stream object in the appropriate places. For example, for faster default output the implementation can cash in the printer the procedures for printing characters and strings to the current output-stream. If the variable is changed, the cash has to be updated which means either that changes to some variables are noticed (some variables are special, which I think is a bad idea), or that the printer must check every time it is invoked. Since with-output-from..., etc, provide ways of redirecting output, and furthermore, a procedure set-current-output-stream! can be provided, there is no functionality lost and the implementation has more freedom. 3. Having functions rather than special forms is in general good, especially since we have not decided whether special form (and macro) names are scoped in the same environment or in a different one, and other such issues. Note that these functions parallel call-with-current-continuation on which we agreed at the workshop, and the reasons are mostly the same. 4. I don't particularly care about the name for is-character-available? (listen?), but there is an important issue which I believe has not been raised. On interactive streams users may have the possibility of erasing typed characters. What happens if listen? returns true and then the user erases the character? The program which invoked listen? will probably attempt to read a character thinking that it will not hang but it will. We had this bug and went to a fair amount of hair to get it fixed. I see two possible solutions to this: - Listen? locks the character if it returns true. This is ugly since read-character may not be performed for a while, and may even be bypassed (error exit, etc.) - Listen? is polymorphic and returns either '() or a character object if one is available. This assumes that the set of character objects is disjoint from the set { '() }. No further call to read is needed. Listen? would then more appropriately be called non-blocking-read-char. I advocate for this solution. 5. We have not talked at all about keyboard interrupts. It seems to me that any reasonable implementation should provide a way for the user to (at least) abort an infinite loop. It would be nice if we had a simple standard mechanism for doing this, but implementations would be free to have more keyborad interrupts (we currently have 5 by default). I propose that we choose some control character (^G, for example) which on all implementations and unless inhibited (an editor may want to do this) will interrupt Scheme and make it return to the top-level read-eval-print loop.  Received: from csnet-relay by MIT-MC.ARPA; 16 MAR 85 13:16:44 EST Received: from unc by csnet-relay.csnet id al00871; 16 Mar 85 13:15 EST Received: by unc (4.12/4.7) id AA06384; Sat, 16 Mar 85 11:38:25 est Date: Sat, 16 Mar 85 11:38:25 est From: Kent Dybvig Message-Id: <8503161638.AA06384@unc> To: scheme@mit-mc.ARPA Subject: Re: I/O proposal I would also like the name "read-char-ready?" instead of "listen?".  Received: from csnet-relay by MIT-MC.ARPA; 16 MAR 85 13:16:38 EST Received: from unc by csnet-relay.csnet id ak00871; 16 Mar 85 13:14 EST Received: by unc (4.12/4.7) id AA03236; Sat, 16 Mar 85 07:34:57 est Date: Sat, 16 Mar 85 07:34:57 est From: Kent Dybvig Message-Id: <8503161234.AA03236@unc> To: scheme@mit-mc.ARPA Subject: Re: I/O proposal I think the overall proposal is quite nice. I do have a couple of remarks and minor suggestions, though. 1. I would prefer a settable *eof* variable. It is useful sometimes to specify the value returned on eof. I also think "eof?" is unnecessarily complex (giving implementors free reign is not the most important issue here). Besides, if you ask ten people what they think the function "eof?" does, chances are all ten will say, "it takes a file as input and returns true iff the file is at eof." No big deal here, though. 2. I do not think the programmer should be encouraged to change the default input/output streams. This leads to confusing or even dangerous behavior when an error or interrupt occurs. The exception handler normally wants to operate in the same environment as the interrupted routine, in order that the user can query the environment (perform stack walkbacks, check values of variables, etc.). Imagine if the handler prints a message and queries the user but receives its input from a disk file because the default input file has been rebound, e.g. "Really delete *.*? (y/n): ". For this reason, I think the names should be "default-input-stream" and "default-output-stream", rather than "current-...". Of course, "with-input-from-file" and "with-output-to-file" should not be supported. I have had experience with this very issue, and troublesome problems really do occur. I do not want to write every piece of code to guard against someone changing the default input/output files on me. I guess we could provide "console-input-stream" and "console-output-stream" functions, but this seems to complicate things unnecessarily. I think it's a good practice anyway to use explicit stream arguments. By the way, why are these functions instead of variables? Isn't this (only perhaps) helping implementrs at the expense of added complexity? 3. Why have "call-with-input-file" and "call-with-output-file" rather than an expression-oriented syntax? I think that the extra level of nesting will be nasty and people will likely just define syntactic extensions to get rid of it anyway. (reference my earlier note) I propose: (with-input-file ( ) form ...) and (with-output-file ( ) form ...) with the same semantics. Each set can easily be defined in terms of the other, but if one is standard it should be the simplest one to use. 4. I prefer the name "write-char" to "display-char". I take it the rationale is that "write-char" might imply that it may be read back in by the function read as a character. I don't think this is really going to cause confusion; after all, it may be read back in by read-char, so read-char/write-char is parallel to to read/write. ..Kent  Received: from MIT-OZ by MIT-MC via Chaosnet; 15 MAR 85 10:59:37 EST Date: Fri, 15 Mar 1985 10:58 EST Message-ID: From: CPH%MIT-OZ@MIT-MC.ARPA To: David Bartley Cc: Scheme@MIT-MC Subject: I/O proposal In-reply-to: Msg of 14 Mar 1985 10:52-EST from David Bartley Date: Thursday, 14 March 1985 10:52-EST From: David Bartley -- DISPLAY-CHAR seems to be a redundant, restricted form of DISPLAY. Since DISPLAY is essential, DISPLAY-CHAR should be dropped. I don't agree. I think that it is good to have a balanced pair of procedures for doing character-level I/O. -- I oppose the term "stream" and thus the names STREAM?, CURRENT-INPUT- STREAM, etc., for the reasons Gerry gave. We use "port". Ditto, except that we use "stream" and will cheerfully change it. -- With my Pascal (and English) background, I can't help but think of features named with "with", like WITH-INPUT-FROM-FILE, as syntax, not procedures. [Did you notice how I began the previous sentence?] I would prefer a special form: (WITH-INPUT-FROM-FILE exp ...). This is more consistent with CL's macro WITH-INPUT-FROM-STRING. The same goes for WITH-OUTPUT-FROM-FILE. I disagree -- proliferation of special forms is a communicable disease.  Received: from csnet-relay by MIT-MC.ARPA; 14 MAR 85 15:27:14 EST Received: from ti-csl by csnet-relay.csnet id a021223; 14 Mar 85 14:58 EST Date: 14 Mar 1985 0952-CST From: David Bartley Subject: Re: I/O proposal To: willc%indiana.csnet@csnet-relay.arpa, scheme@mit-mc.ARPA cc: Bartley%ti-csl.csnet@csnet-relay.arpa In-Reply-To: Your message of 12-Mar-85 1001-CST Received: from csl60 by ti-csl; Thu, 14 Mar 85 11:04 CST Will-- A few questions and remarks about your I/O proposal: -- What happened to the names PRIN1 and PRINC? Are your WRITE and DISPLAY meant to be equivalent, respectively, to PRIN1 and PRINC? As I understand page 382 of the Common LISP (CL) book, your WRITE is not incompatible with CL's. Likewise, CL doesn't seem to have a DISPLAY. However, I am bothered by my intuition that DISPLAY should be a terminal-oriented operation. Bottom line: I vote to go with WRITE and DISPLAY, but the names PRIN1 and PRINC should be reserved as well (as optional aliases). The PRINT operation should also be optional, with the CL-compatible meaning we agreed on at the workshop. -- DISPLAY-CHAR seems to be a redundant, restricted form of DISPLAY. Since DISPLAY is essential, DISPLAY-CHAR should be dropped. -- The name LISTEN? also is slightly unintuitive to me; how about INPUT?, or MORE-INPUT?, or something with "peek" in it. -- Does READ-CHAR wait for interactive input or return an eof token if nothing has been buffered up from the keyboard (or comm device, etc)? -- I oppose the term "stream" and thus the names STREAM?, CURRENT-INPUT- STREAM, etc., for the reasons Gerry gave. We use "port". -- With my Pascal (and English) background, I can't help but think of features named with "with", like WITH-INPUT-FROM-FILE, as syntax, not procedures. [Did you notice how I began the previous sentence?] I would prefer a special form: (WITH-INPUT-FROM-FILE exp ...). This is more consistent with CL's macro WITH-INPUT-FROM-STRING. The same goes for WITH-OUTPUT-FROM-FILE. -- I don't mind your OPEN- and CLOSE- operations, as long as they remain optional, but I feel bound to implement OPEN and CLOSE compatibly with CL. -- I like your approach to eof objects. -- You have no READ-ATOM. Is there general agreement that READ-ATOM is unimportant? We have it only by inheritance from Scheme 84. Regards, David Bartley -------  Date: 14 March 1985 13:23-EST From: Jonathan A Rees Subject: interaction of LOAD and CURRENT-INPUT-STREAM To: mw%brandeis.csnet @ CSNET-RELAY cc: SCHEME @ MIT-MC Date: 12 Mar 1985 11:22-EST From: mw%brandeis.csnet at csnet-relay.arpa 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. I don't see any reason for LOAD and CURRENT-INPUT-STREAM to interact, and I think it is much better for them not to. If they don't interact you have a much better invariants on each, e.g.: doing LOAD on a file is the same as doing LOAD on a file consisting of the string "(begin " appended to the file appended to the string ")". What's wrong with writing (define! foo ...) (define! bar (foo ' ..complicated data to be processed by foo during load.. )) ? This way you don't need to do explicit I/O at all. ... And incidentally, I don't remember the rationale for having both DEFINE! and DEFINE. I understand why DEFINE shouldn't have hairy syntax, but what does DEFINE! give you that the stripped-down DEFINE doesn't? Jonathan