1979年: Design of LISP-based Processors, or SCHEME: A Dialect of LISP, or Finite Memories Considered Harmful, or LAMBDA: The Ultimate Opcode.[44]
1980年: Compiler Optimization Based on Viewing LAMBDA as RENAME + GOTO. AI: An MIT Perspective.[45]
1980年: Design of a Lisp-based Processor. CACM. 23. 11.[46]
標準化
Scheme的業界標準,是由專門的掌控委員會發表的《第n次修訂的演算法語言Scheme報告》(Revisedn Report on the Algorithmic Language Scheme),这种标题形式参照了ALGOL 60标准文档的标题[47]。Scheme曾經由IEEE標準化為IEEE 1178–1990,它于2019年11月07日停用[48]。
1998年通过的R5RS是現在被普遍接受的標準[49]。2007年通過的R6RS[50],做出了很大的變動[51],Scheme社區中有使用者指責它在堆積華而不實的功能[52][53]。Scheme掌控委員會決定在R7RS中,將Scheme分化為兩個獨立而兼容的語言[54]:一個是2013年通過的R7RS-small[55],它為教學場合提供对IEEE/R5RS標準的及时更新,R7RS-small目前已经有了一些实现支持[56];而另一個是作为标准库合集的R7RS-Large[57],它包含了各种提议被接纳并进入冻结状态的Scheme实现要求(英语:Scheme Requests for Implementation)(SRFI),以此為實際編程場合提供对R6RS標準的继续完善,Larceny和Chibi[3]提供了R7RS-Large过渡草案红色版(即数据结构部份)的全部SRFI的实现。
Scheme與其他Lisp方言的列表,都是基於最基礎的數據結構有序對(pair)。Scheme从它的Lisp先辈继承了一组丰富的列表处理原始运算,比如cons、car(英语:CAR and CDR)和cdr(英语:CAR and CDR)。Scheme的變數都使用動態強型別系統,Scheme中的过程都是頭等物件,即头等函数,因此过程可以作为值赋值给变量,或作为实际参数传递给过程。
这里的(let ((var expr) ...) body ...)称为“模式”,模式中起始的let被称为“关键字”,syntax-rules ()的空括号中可添入作为辅助关键字的叫做“文字”的标识符,var、expr和body称为“模式变量”,...是称为“省略号”的标识符,它表示所跟随的子模式可以匹配零或多个输入中的元素;而((lambda (var ...) body ...) expr ...)称为“模板”。使用上述定义的let,一个Scheme实现可以将(let ((a 1)(b 2)) (+ b a)),重写为((lambda (a b) (+ b a)) 1 2),这将实现任务缩减为编码过程实例化的工作。
一个正式的λ系统,拥有一些公理和一个完备的推理规则。这有助于使用数理逻辑和工具来进行分析。在这个系统中,演算可以被看作直接的演绎。λ演算的语法,来自用x, y, z, ...、圆括号、空格、点号和符号λ形成的递归表达式[63]。λ演算的功能包括:首先,充当强力的数理逻辑的起点。其次,它可以缩减编程者在考虑实现细节上的要求,因为它可以用于模拟机器求值。最后,λ演算建立了一个坚实的元理论[64]。
在Scheme中,lambda關鍵字被用於定義匿名过程,并且使用define基础形式来定义命名过程,即将一个lambda过程绑定到指名的全局变量[65]。在Scheme中,(define (foo x) (+ x 1))與(define foo (lambda (x) (+ x 1)))
,在語法上是等同的。例如有序對可以表示為[66]:
Scheme拥有一个迭代构造do[72],但是在Scheme中更推崇的惯用法(英语:Programming idiom),是使用尾递归来表达迭代[73]。遵循标准的Scheme实现被要求优化尾递归,从而支持无限数量的活跃尾递归(R5RS sec. 3.5[49]),这个性质被Scheme报告描述为“适当”(proper)尾递归,它使得Scheme编程者可以安全的使用递归结构来书写迭代算法,这在很多时候是更符合直觉的。尾递归过程和命名let形式,提供对使用尾递归的迭代的支持。
在Scheme中续体是头等对象[74]。自从R2RS开始[58],Scheme提供了过程call-with-current-continuation(英语:call-with-current-continuation),简写为call/cc,它捕获当前续体,将其包装成逃脱(escape)过程,再绑定到编程者提供的一个过程的唯一形式参数上。如果逃脱过程在后来某个时候被调用,它会放弃在后来时候正生效的任何续体,转而使用在创建这个逃脱过程之时生效的那个续体。逃脱过程与原本调用call/cc的续体,接受相同数目的实际参数;除了用call-with-values创建的续体,所有续体恰好接受一个值(R5RS sec. 6.4)[49]。
在R5RS中,给出了delay和force的推荐实现,将promise实现为没有实际参数的一个过程(thunk(英语:thunk)),并使用记忆化来确保它永远只求值一次,与调用force的次数无关(R5RS sec. 6.4)[49]。在R6RS标准中,它们不再是原始运算,转而作为R5RS兼容库的一部份提供(rnrs r5rs (6))。
在R5RS标准中,不要求Scheme实现去实现整个数值塔,而是它们必须实现“符合实现用途和Scheme语言精神二者的连贯子集”(R5RS sec. 6.2.3)[49]。新的R6RS标准要求实现整个数值塔,并且“精确整数对象和精确有理数对象实际上有无限的大小和精度,并且对于实现特定过程...所以它们在得到精确实际参数时总是返回精确结果”(R6RS sec. 3.4, sec. 11.7.1)[50]。
;; NB: with-output-to-file is an optional procedure in R5RS(let((hello0(lambda()(display"Hello world")(newline))))(with-output-to-file"helloworldoutputfile"hello0))
Will Clinger, Marc Feeley, Chris Hanson, Jonathan Rees and Olin Shivers. Scheme Steering Committee Position Statement. Scheme Steering Committee. 2009-08-20 [2011-12-19]. (原始内容存档于2009-08-26). Scheme has the unhappy distinction of being the world's most unportable programming language. It is almost misleading to call Scheme a "programming language;" it would be more accurate to characterise Scheme as a family of dialects, all loosely related by the common features of lexical scope, dynamic typing, list structure, higher-order functions, proper tail-recursion, garbage collection, macros, and (some form of) s-expression based lexical syntax.
John McCarthy. History of Lisp(PDF). Artificial Intelligence Laboratory, Stanford University. 12 February 1979 [2022-11-17]. (原始内容存档(PDF)于2020-11-07). To use functions as arguments, one needs a notation for functions, and it seemed natural to use the λ-notation of Church (1941). I didn’t understand the rest of his book, so I wasn’t tempted to try to implement his more general mechanism for defining functions. Church used higher order functionals instead of using conditional expressions. Conditional expressions are much more readily implemented on computers. David Turner(英语:David Turner (computer scientist)). Some History of Functional Programming Languages(PDF). [2021-11-10]. (原始内容(PDF)存档于2020-04-15). LISP was not based on the lambda calculus, despite using the word “LAMBDA” to denote functions. At the time he invented LISP, McCarthy was aware of (Church 1941) but had not studied it. The theoretical model behind LISP was Kleene’s theory of first order recursive functions. (McCarthy made these statements, or very similar ones, in a contribution from the floor at the 1982 ACM symposium on LISP and functional programming in Pittsburgh. No written version of this exists, as far as know.)
Gerald J. Sussman, Guy L. Steele Jr.The First Report on Scheme Revisited. 1998 [2021-11-07]. (原始内容存档于2022-04-06). At about this time, Carl Hewitt was developing his theory of actors as a model of computation. This model was object-oriented and strongly influenced by Smalltalk. Every object was a computationally active entity capable of receiving and reacting to messages. The objects were called actors, and the messages themselves were also actors. Every computational entity was an actor and message-passing was the only means of interaction. An actor could have arbitrarily many acquaintances; that is, it could “know about” other actors and send them messages or send acquaintances as (parts of) messages. Hewitt then went on to model many traditional control structures as patterns of message-passing among actors. Functional interactions were modeled with the use of continuations; one might send the actor named “factorial” a message containing two other actors: the number 5 and another actor to which to send the eventually computed value (presumably 120). While Conniver made control points into first-class data, the actors model went to the logical extreme by making everything be first-class data.
Carl Hewitt, Peter Bishop, Irene Greif, Brian Smith, Todd Matson, Richard Steiger. Actor induction and meta-evaluation. 1973 [2022-11-15]. (原始内容存档于2022-11-15). “For some years we have been constructing a series of languages to express our evolving understanding of the above semantic issues. The latest of these
is called PLANNER-73. ……
We shall assume that the reader is familiar with advanced pattern matching languages such as SNOBOL4, CONVERT, PLANNER-71, QA4, and POPLER.
We shall use (%A M%) to indicate sending the message M to the actor A. We shall use [s1 s2 … sn] to denote the finite squence s1, s2, … sn. ……
Identifiers can be created by the prefix operator =. ……
An expression (=> pattern body) is an abbreviation for (receive(message pattern) body) where receive is a more general actor that is capable of bindinq elements of the action in addition to the message.
Evaluating (%(=> pattern body) the-message%), i.e. sending (=> pattern body) the-message,
will attempt to match the-message against pattern, If the-message is not of the form specified by pattern, then the actor is NOT-APPLICABLE to the-message. If the-message matches pattern, the body is evaluated. ……
Conceptually at least a new actor is created every time a message is sent. Consider sending to a target T a message M and a continuation C.
(send T(messageM[#continuationC]))
The transmission (%T M%) is an abbreviation for the above where C is defaulted to be the caller. If target T is the following:
then the the-body is evaluated in an environment where the-message is bound to M and the-continuation is bound to C. ……
Many actors who are executing in parallel can share the same continuation. They can all send a message [“return”] to the same continuation. ”
Irene Greif, Carl Hewitt. Actor semantics of PLANNER-73. 1975 [2022-11-15]. (原始内容存档于2022-11-15).
Gerald J. Sussman, Guy L. Steele Jr.The First Report on Scheme Revisited. 1998 [2021-11-07]. (原始内容存档于2022-04-06). Hewitt and his students developed and implemented in Lisp a new language to make concrete the actor model of computation. This language was first called Planner-73 but the name was later changed to PLASMA (PLAnner-like System Modeled on Actors). It was at this point that we (Sussman and Steele) put our heads together. (Steele had just become a graduate student at MIT, but he had been following this progression of language designs because he had had a part-time job at MIT Project MAC as one of the maintainers of MacLisp.) We wanted to better understand Hewitt’s actors model but were having trouble relating the actors model and its unusual terminology to familiar programming notions. We decided to construct a toy implementation of an actor language so that we could play with it. Using MacLisp as a working environment, we wrote a tiny Lisp interpreter and then added mechanisms for creating actors and sending messages. We wanted to better understand Hewitt’s actors model but were having trouble relating the actors model and its unusual terminology to familiar programming notions. We decided to construct a toy implementation of an actor language so that we could play with it. Using MacLisp as a working environment, we wrote a tiny Lisp interpreter and then added mechanisms for creating actors and sending messages.
彼得·兰丁. The mechanical evaluation of expressions(PDF). 1964 [2022-11-17]. (原始内容存档(PDF)于2022-11-16). Also we represent the value of a λ-expression by a bundle of information called a "closure," comprising the λ-expression and the environment relative to which it was evaluated. We must therefore arrange that such a bundle is correctly interpreted whenever it has to be applied to some argument. More precisely: a closure has an environment part which is a list whose two items are: ⑴ an environment ⑵ an identifier or list of identifiers, and a control part which consists of a list whose sole item is an AE. The value relative to E of a λ-expression X is represented by the closure denoted by constructclosure((E, bvX), unitlist(bodyX))
Joel Moses(英语:Joel Moses). The Function of FUNCTION in LISP, or Why the FUNARG Problem Should Be Called the Environment Problem(pdf). June 1970 [2009-10-27]. AI Memo 199. (原始内容存档于2010-05-23). The use of free variables is a very powerful computational idea. In many situations it is possible to avoid using free variables by supplying a function with additional arguments. This can be quite cumbersome. …… When a function uses free variables or when it calls functions which use them, then the value of this function is not completely determined by its arguments, since the value might depend on the values of the free variables. Thus in order to completely specify a computation, one has to specify a function and its arguments in addition to an environment in which the values of the free variables can be determined. …… An important point which must be realized about functional arguments (abbreviated FUNARGs) is that two different environments are involved in such cases. The first environment is the one which is in effect when then FUNARG is bound as an argument. We shall call this the binding environment. The second environment is the one that is in effect when the FUNARG is activated as a function. We shall call this the activation environment. …… Consider now what it would require for the system to restore the binding environment for functional arguments. It would require knowing where in the stack or "alist" the binding environment exists through some pointer to it. Supplying such pointer is a function FUNCTION in LISP. That is when one transmits a functional argument f which is to be evaluated in its binding environment, then one uses FUNCTION(f). FUNCTION will prevent its argument f from being evaluated, just as QUOTE would. The result of FUNCTION will be a structure which not only contains a reference to the function f, but also contains a pointer to the binding environment. Thus at the FUNARG's activation time we will be able to use the current place in the stack up to the point where the binding environment exists, thus skipping over any values in the activation environment which differ from those in the binding environment. …… The points we have made so far are: 1) Free variables in function definitions require that one must have an environment in order to be able to evaluate a function. 2) When one uses functional arguments, the the question arises as to which environment one chooses in order to evaluate the function, the binding environment or the activation environment. 3) When one returns functions (which require the values of free variables for their evaluation) then one must, in general, save the binding environment and thus create a stack which is, in fact, a tree. …… LISP allows the user to choose between the binding environment and the activation environment by allowing a function to be bound with FUNCTION or with QUOTE. QUOTE will force the activation environment to be used in evaluating the function. A useful metaphor for the difference between FUNCTION and QUOTE in LISP is to think of QUOTE as a porous or an open covering of the function since free variables escape to the current environment. FUNCTION acts as a closed or nonporous covering (hence the term "closure" used by Landin). Thus we talk of "open" Lambda expressions (functions in LISP are usually Lambda expressions) and "closed" Lambda expressions. …… My interest in the environment problem began while Landin, who had a deep understanding of the problem, visited MIT during 1966-67. I then realized the correspondence between the FUNARG lists which are the results of the evaluation of "closed" Lambda expressions in LISP and ISWIM's Lambda Closures.
Gerald J. Sussman, Guy L. Steele Jr.The First Report on Scheme Revisited. 1998 [2021-11-07]. (原始内容存档于2022-04-06). Sussman had just been studying Algol. He suggested starting with a lexically scoped dialect of Lisp, because that seemed necessary to model the way names could refer to acquaintances in PLASMA. Lexical scoping would allow actors and functions to be created by almost identical mechanisms. Evaluating a form beginning with the word lambda would capture the current variable-lookup environment and create a closure; evaluating a form beginning with the word alpha would also capture the current environment but create an actor. Message passing could be expressed syntactically in the same way as function invocation. The difference between an actor and a function would be detected in the part of the interpreter traditionally known as apply. A function would return a value, but an actor would never return; instead, it would typically invoke a continuation, another actor that it knew about. Our interpreter also provided the necessary primitives for implementing the internal behavior of primitive actors, such as an addition operator that could accept two numbers and a continuation actor.
Gerald J. Sussman, Guy L. Steele Jr.The First Report on Scheme Revisited. 1998 [2021-11-07]. (原始内容存档于2022-04-06). This led us to three important ideas: • First, we realized that all the patterns of control structure that Hewitt had described in terms of actors could equally well be described by the λ-calculus. …… • Second, we realized that the λ-calculus — a small, simple formalism — could serve as the core of a powerful and expressive programming language. (Lisp had adopted the λ-notation for functions but had failed to support the appropriate behavior for free variables. The original theoretical core of Lisp was recursion equations, not the λ-calculus.) …… • Third, we realized that in our quest for the “ultimate AI language” we had come full circle. As the MIT school had struggled to find more and more powerful ways to express and manipulate control structure to support heuristic search, we had progressed from Lisp to CONVERT to Planner to Conniver to PLASMA to a simple variation of Lisp!
Gerald J. Sussman, Guy L. Steele Jr.. Scheme: An Interpreter for Extended Lambda Calculus. 维基文库. 1975 (英文). Inspired by ACTORS [Greif and Hewitt][Smith and Hewitt], we have implemented an interpreter for a LISP-like language, SCHEME, based on the lambda calculus [Church], but extended for side effects, multiprocessing, and process synchronization. The purpose of this implementation is tutorial. We wish to: ⑴ alleviate the confusion caused by Micro-PLANNER(英语:Planner (programming language)), CONNIVER, etc. by clarifying the embedding of non-recursive control structures in a recursive host language like LISP. ⑵ explain how to use these control structures, independent of such issues as pattern matching and data base manipulation. ⑶ have a simple concrete experimental domain for certain issues of programming semantics and style. LISP code for the SCHEME interpreter by Gerald Jay Sussman and Guy Lewis Steele, Jr. [2022-11-14]. (原始内容存档于2022-11-14).
Gerald J. Sussman, Guy L. Steele Jr.The First Report on Scheme Revisited. 1998 [2021-11-07]. (原始内容存档于2022-04-06). We were very pleased with this toy actor implementation and named it “Schemer” because we thought it might become another AI language in the tradition of Planner and Conniver. However, the ITS operating system had a 6-character limitation on file names and so the name was truncated to simply SCHEME and that name stuck.
Gerald J. Sussman, Guy L. Steele Jr.The First Report on Scheme Revisited. 1998 [2021-11-07]. (原始内容存档于2022-04-06). We concluded that actors and closures were effectively the same concept. (Hewitt later agreed with this, but noted that two types of primitive actors in his theory, namely cells (which have modifiable state) and synchronizers (which enforce exclusive access), cannot be expressed as closures in a lexically scoped pure Lisp without adding equivalent primitive extensions.) Carl Hewitt(英语:Carl Hewitt). ActorScript: Industrial strength integration of local and nonlocal concurrency for Client-cloud Computing. 2009 [2022-12-23]. (原始内容存档于2022-12-23). In summary, Sussman and Steele [1975] mistakenly concluded “we discovered that the 'Actors' and the lambda expressions were identical in implementation.” The actual situation is that the λ-calculus is capable of expressing some kinds of sequential and parallel control structures but, in general, not the concurrency expressed in the Actor model. On the other hand, the Actor model is capable of expressing everything in the λ-calculus and more [Hewitt 2008f].
Guy L. Steele Jr., Gerald J. Sussman. The Revised Report on SCHEME: A Dialect of LISP(PDF). 1978 [2022-11-18]. (原始内容存档(PDF)于2022-12-12). SCHEME is a dialect of LISP. It is an expression-oriented, applicative order, interpreter-based language which allows one to manipulate programs as data. It differs from most current dialects of LISP in that it closes all lambda-expressions in the environment of their definition or declaration, rather than in the execution environment. This has the consequence that variables are normally lexically scoped, as in ALGOL. However, in contrast with ALGOL, SCHEME treats procedures as a first-class data type. They can be the values of variables, the returned values of procedures, and components of data structures. Another difference from LISP is that SCHEME is implemented in such a way that tail-recursions execute without net growth of the interpreter stack. The effect of this is that a procedure call behaves like a GOTO and thus procedure calls can be used to implement iterations, as in PLASMA.
Gerald J. Sussman, Guy L. Steele Jr.The First Report on Scheme Revisited. 1998 [2021-11-07]. (原始内容存档于2022-04-06). We were actually trying to build something complicated and discovered, serendipitously, that we had accidentally designed something that met all our goals but was much simpler than we had intended. We thought that Scheme would turn out to be the next in a long series of programming languages that had been designed at MIT over the course of 17 years. …… In this process we learned some great lessons. The λ-calculus can be seen as a universal glue by which compound constructs can be built inductively from simpler ones and a set of primitives. …… The essence of the matter is that the λ-calculus provides a way of abstracting entities of any particular type and then combining such abstractions with other entities to make new entities of that same type, which are instances of the abstractions. …… Given the right primitives, the λ-calculus can serve as a foundation for the abstraction and instantiation of virtually any kind of complex conceptual structure. We also were struck by the great importance of names as a means of reference. …… Naming seems to correspond to some important cognitive mechanism that is, if not innate, then at least extremely well-developed in our culture. The λ-calculus embodies a provably coherent theory of naming.
J.W. Backus; F.L. Bauer; J.Green; C. Katz; J. McCarthy P. Naur; et al. Revised Report on the Algorithmic Language Algol 60. Numerische Mathematik, Communications of the ACM, and Journal of the British Computer Society. January–April 1960 [2012-08-09]. (原始内容存档于2007-06-25).
Richard Kelsey, William Clinger(英语:William Clinger (computer scientist)), Jonathan Rees; et al. Revised5 Report on the Algorithmic Language Scheme. Higher-Order and Symbolic Computation. August 1998, 11 (1): 7–105 [2007-01-04]. doi:10.1023/A:1010051815785. (原始内容存档于2007-01-05) (英语). Scheme is a statically scoped and properly tail-recursive dialect of the Lisp programming language invented by Guy Lewis Steele Jr. and Gerald Jay Sussman. It was designed to have an exceptionally clear and simple semantics and few different ways to form expressions. A wide variety of programming paradigms, including imperative, functional, and message passing styles, find convenient expression in Scheme. 引文格式1维护:显式使用等标签 (link)
Michael Sperber, R. Kent Dybvig, Matthew Flatt(英语:Matthew Flatt), Anton Van Straaten; et al. Revised6 Report on the Algorithmic Language Scheme. Scheme Steering Committee. August 2007 [2009-10-20]. (原始内容存档于2013-06-25). This report is accompanied by a report describing standard libraries; references to this document are identified by designations such as “library section” or “library chapter”. It is also accompanied by a report containing non-normative appendices. A fourth report gives some historical background and rationales for many aspects of the language and its libraries. 引文格式1维护:显式使用等标签 (link)
R7RS-Large Interim Red Edition. 2020-03-17 [2022-11-13]. (原始内容存档于2022-11-13). The Revised7 Report on the Algorithmic Language Scheme (“R7RS”) intentionally describes a small language, suitable for implementing on reduced hardware, or for experiments in programming language design and implementation. However, Scheme is used for practical programming, and there a minimal language is not a help. Additional data structures and programming idioms make the programmer’s job easier (if they are well-designed!). Therefore, as part of the R7RS design process, it was agreed that a subsequent report would appear on “R7RS-Large”, a collection of Scheme libraries that are useful across a wide variety of problems. The decision of what was to appear in R7RS-Large was left to the community, and a strategy was adopted: proposals were published as SRFIs (Scheme Requests For Implementation, http://srfi.schemers.org), and the community would then vote on which would be adopted. As a result, R7RS-Large will be developed in several stages, depending upon the support from the community for each proposal. At each stage, several SRFIs are frozen, and implementers and users may then rely upon those libraries being in the final product. (In a few cases, a library might be revisited, with a subsequent stage adopting a completely backward-compatible new version of that library; this has already happened with generators.) When this process has completed, the resulting interim reports will be collated and reorganized into a final document.
William Clinger(英语:William Clinger (computer scientist)). The Revised Revised Report on Scheme or An Uncommon Lisp(PDF). 1985 [2022-11-17]. (原始内容存档(PDF)于2022-10-17). Data and procedures and the values they amass, Higher-order functions to combine and mix and match, Objects with their local state, the message they pass, A property, a package, the control of point for a catch - In the Lambda Order they are all first-class. One thing to name them all, one things to define them, one thing to place them in environments and bind them, in the Lambda Order they are all first-class. …… Scheme shares with Common Lisp 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.
Gerald J. Sussman, Guy L. Steele Jr.The Revised Report on SCHEME: A Dialect of LISP. 1978. System-Provided Extensions - Some standard syntactic extension are provided by convenience in ordinary programming. They are distinguished from other magic words in that they are semantically defined in terms of others rather then being primitive {Note FEXPR(英语:fexpr)s Are Okay by Us}. For expository purposes they are described here pattern-matching/production-rules kind of language. takes advantage of the definition of list notation: (A B C) = (A . (B . (C . NIL))). Thus the pattern (x . r) matches (A B C), with x = A and r = (B C). the ordering of the "productions" is significant; the first one which matches is to be used.
Kent M. Pitman(英语:Kent Pitman). The Revised Maclisp Manual. 1983, 2007 [2022-12-16]. (原始内容存档于2022-12-16). In the interpreter “variables” are implemented as atomic symbols which possess “shallow-bound” value cells. The continual manipulation of value cells would decrease the efficiency of compiled code, so the compiler defines two types of variables: “special variables” and “local variables.” Special variables are identical to variables in the interpreter. Local variables are more like the variables in commonly-used algebraic programming languages such as Algol or PL/I. A local variable no longer has an associated atomic symbol; thus it can only be referred to from the text of the same function that bound it. The compiler creates local variables for PROG variables, DO variables, and LAMBDA variables, unless directed otherwise. The compiled code stores local variables in machine registers or in locations within a stack. The principal difference between local variables and special variables is in the way a binding of a variable is compiled. (A binding has to be compiled when a PROG, DO, or LAMBDA expression is compiled, and for the entry to a function which has LAMBDA variables to be bound to its arguments.) If the variable to be bound has been declared to be special, the binding is compiled as code to imitate the way the interpreter binds variables: the value of the atomic symbol is saved and a new value is stored into its value cell. If the variable to be bound has not been declared special, the binding is compiled as the declaration of a new local variable. Code is generated to store the value to which the variable is to be bound into the register or stack-location assigned to the new local variable. This runs considerably faster than a special binding. Although a special variable is associated with an atomic symbol which has the name of the variable, the name of a local variable appears only in the input file - in compiled code there is no connection between local variables and atomic symbols. Because this is so, a local variable in one function may not be used as a “free variable” in another function since there is no way for the location of the variable to be communicated between the two functions. When the usage of a variable in a program to be compiled does not conform to these rules, i.e. it is somewhere used as a “free variable,” the variable must be declared special. There are two common cases in which this occurs. One is where a “global” variable is being used, i.e. a variable which is SETQ'ed by many functions but is never bound. The other is where two functions cooperate, one binding a variable and then calling the other one which uses that variable as a free variable.请检查|date=中的日期值 (帮助)
Gerald J. Sussman, Guy L. Steele Jr.. Scheme: An Interpreter for Extended Lambda Calculus. 维基文库. 1975 (英文). There are several important consequences of closing every lambda expression in the environment from which it is passed (i.e., in its "lexical" or "static" environment). First, the axioms of lambda calculus are automatically preserved. Thus, referential transparency(英语:referential transparency) is enforced. This in turn implies that there are no "fluid" variable bindings (as there are in standard stack implementations of LISP such as MacLISP). Second, the upward funarg problem(英语:funarg problem) [Moses] requires that the environment structure be potentially tree-like. Finally, the environment at any point in a computation can never be deeper than the lexical depth of the expression being evaluated at that time; i.e., the environment contains bindings only for variables bound in lambdas lexically surrounding the expression being evaluated. This is true even if recursive functions are involved. ……Furthermore, it is not even necessary to scan the environment for the variable, since its value must be in a known position relative to the top of the environment structure; this position can be computed by a compiler at compile time on the basis of lexical scope.
John McCarthy, Paul W. Abrahams, Daniel J. Edwards, Timothy P. Hart, Michael I. Levin. LISP 1.5 Programmer's Manual(PDF) 2nd. MIT Press. 1985 [1962] [2021-10-25]. ISBN 0-262-13011-4. (原始内容(PDF)存档于2021-03-02). The variables in a lambda expression are dummy or bound variables because systematically changing them does not alter the meaning of the expression. Thus λ[[u;v];v2+u] means the same thing as λ[[x;y];y2+x]. We shall sometimes use expressions in which a variable is not bound by a lambda. For example, in the function of two variables λ[[x;y];xn+yn] the variable n is not bound. This is called a free variable. It may be regarded as a parameter. Unless n has been given a value before trying to compute with this function, the value of the function must be undefined. …… When the compiler is called upon to compile a function, it looks for an EXPR or FEXPR on the property list of the function name. The compiler then translates this S-expression into an S-expression that represents a subroutine in the LISP Assembly Language (LAP). LAP then proceeds to assemble this program into binary program space. Thus an EXPR, or an FEXPR, has been changed to a SUBR or an FSUBR, respectively. …… Free variables in compiled functions must be declared before the function is compiled. …… A variable is bound in a particular function when it occurs in a list of bound variables following the word LAMBDA or PROG. Any variable that is not bound is free. …… When a variable is used free, it must have been bound by a higher level function. If a program is being run interpretively, and a free variable is used without having bee bound on a higher level, error diagnostic *A 8* will occur. If the program is being run complied, the diagnostic may not occur, and the variable may have value NIL. There are three types of variables in compiled functions: ordinary variables, SPECIAL variables, and COMMON variables. SPECIAL and COMMON variables must be declared before compiling. Any variable that is not declared will be considered an ordinary variable. When functions are translated into subroutines, the concept of a variable is translated into a location where an argument is stored. If the variable is an ordinary one, then a storage location for it is set up on the push-down list. Other functions cannot find this private cell, making it impossible to use it as a free variable. SPECIAL variables have the indicator SPECIAL on their property lists Following the indicator there is a pointer to a fixed cell. When this variable is bound, the old value is saved on the push-down list, and the current value is stored in the SPECLAL cell. When it is no longer bound, the old value must be restored. When a function uses this variable free, then the quantity in the SPECIAL cell is picked up. SPECIAL variables are declared by using the pseudo-function special[a], where a is a list of variable names. …… SPECIAL variables are inexpensive and will allow free communication among compiled functions. They do not increase run time significantly. SPECIAL variables cannot be communicated between the interpreter and compiled functions. COMMON variables have the flag COMMON on their property lists; however, this is only used to inform the compiler that they are COMMON, and is not needed at run time. COMMON variables are bound on an a-list by the compiled functions. When they are to be evaluated, eval is given this a-list. This happens at run time. The use of COMMON variables will slow down any compiled function using them. However, they do provide complete communication between interpreted and compiled functions. COMMON variables are declared by common[a], where a is a list of variable names. …… LISP is designed in such a way that all functions for which the possibility of recursion can exist are in fact recursive. This requires that all temporary stored results related to the computation that is in progress be set aside when a piece of coding is to be used recursively, and that they be later restored. This is done automatically and need not be programmed explicitly. All saving of temporary results in LISP is performed on a linear block of storage called the push-down list. Each set of stored data that is moved onto the push-down list is in a block labeled with its size and the name of the subroutine from which it came. Since it is in the nature of recursion that the first block to be saved is always the last block to be restored, it is possible to keep the push-down list compact. Joel Moses(英语:Joel Moses). The Function of FUNCTION in LISP, or Why the FUNARG Problem Should Be Called the Environment Problem(pdf). June 1970 [2009-10-27]. AI Memo 199. (原始内容存档于2010-05-23). There are several ways in which to maintain the values of free variables (and, thus the computational environment) in order to handle cases like the one above. All of the techniques used involve some increase in time. One approach makes it easy to access the value of a free variable is local. This approach is favored in recent implementations of LISP. We shall call it the "shallow access" approach. Another approach is to make it relatively easy to enter and leave a block in which a free variable is bound, but relatively expensive to access a free variable. This approach is used in the classical "alist" implementation of LISP. Several Algol systems have opted for a similar approach. We shall call this the "deep access" approach. Both of these approaches allow one to modify the value of the variable as well as access it. The access time is approximately the same as modification time in both approaches. Let us consider the "shallow access" approach first. By "shallow access" we mean that the value of a free variable can be obtained in a single fetch from memory. Since the current value may be stored in a difficult-to-determine location up in the stack, a special cell for its current value is used. In many recent LISP implementations this special values cell is stored as a property of the atom structure, and is unique to the free variable. In order to maintain the proper value in the special cell, extra work must be done every time a function or a block is entered in which the variable is an argument or is local. On entering such a function or block one usually saves the old value which is in the special cell on the stack alone with sufficient information to allow one to know from where the stack value came. On leaving such a function or block one must remember to store the value saved in the stack in the special cell. The "deep access" approach forces one to search upward in the stack for the most recent value of the free variable. Such a search may be quite deep and slow if the free variable was bond vary far up in the stack. However, this approach has the advantage of avoiding the need for an extra special value cell. In addition, we shall see later that this approach sometimes allows one to use free variables with greater ease and flexibility than the "shallow access" approach. …… Certain LISP systems use "deep access" in interpreted functions and "shallow access" in compiled functions. if free variables in compiled functions are declared to be COMMON, their values will be stored on the "alist". In this manner one can solve the environment problem. The cost is a very slow interpreter and "deep access" to free variables.
Gerald J. Sussman, Guy L. Steele Jr.. Scheme: An Interpreter for Extended Lambda Calculus. 维基文库. 1975 (英文). DEFINE - This is analogous to the MacLISP DEFUN primitive (but note that the LAMBDA must appear explicitly!). It is used for defining a function in the "global environment" permanently, as opposed to LABELS (see below), which is used for temporary definitions in a local environment. DEFINE takes a name and a lambda expression; it closes the lambda expression in the global environment and stores the closure in the LISP value cell of the name (which is a LISP atom). …… LABELS - We have decided not to use the traditional LABEL primitive in this interpreter because it is difficult to define several mutually recursive functions using only LABEL. The solution, which Hewitt [Smith and Hewitt] also uses, is to adopt an ALGOL-esque block syntax: (LABELS <function definition list> <expression>) This has the effect of evaluating the expression in an environment where all the functions are defined as specified by the definitions list. Furthermore, the functions are themselves closed in that environment, and not in the outer environment; this allows the functions to call themselves and each other recursively. Gerald J. Sussman, Guy L. Steele Jr.The Revised Report on SCHEME: A Dialect of LISP. 1978 [2021-11-07]. (原始内容存档于2022-04-06). Atoms which are not atomic symbols (identifiers) evaluate to themselves. Typical examples of such atoms are numbers, arrays, and strings (character arrays). Symbols are treated as identifiers or variables. They may be lexically bound by lambda-expressions. There is a global environment containing values initially have as their values primitive operations such as, for example, CAR, CONS, and PLUS. SCHEME differs from most LISP systems in that the atom CAR is not itself an operation (in the sense of being an invocable object, e.g. a valid first argument to APPLY), but only has one as a value when considered as an identifier.
Gerald J. Sussman, Guy L. Steele Jr.. Lambda: The Ultimate Imperative. 维基文库. 1976 (英文). Lambda calculus (and related languages, such as "pure LISP") is often used for modelling the applicative constructs of programming languages. However, they are generally thought of as inappropriate for modelling imperative constructs. …… we show how imperative constructs may be modelled by applicative SCHEME constructs. …… Up to now we have thought of SCHEME’s LAMBDA expressions as functions, and of a combination such as (G (F X Y)) as meaning “apply the function F to the values of X and Y, and return a value so that the function G can be applied and return a value ...” But notice that we have seldom used LAMBDA expressions as functions. Rather, we have used them as control structures and environment modifiers. For example, consider the expression: (BLOCK (PRINT 3) (PRINT 4)) This is defined to be an abbreviation for: ((LAMBDA (DUMMY) (PRINT 4)) (PRINT 3)) We do not care about the value of this BLOCK expression; it follows that we do not care about the value of the (LAMBDA (DUMMY) ...). We are not using LAMBDA as a function at all. It is possible to write useful programs in terms of LAMBDA expressions in which we never care about the value of any lambda expression. We have already demonstrated how one could represent any “FORTRAN” program in these terms: all one needs is PROG (with GO and SETQ), and PRINT to get the answers out. The ultimate generalization of this imperative programming style is continuation-passing. …… …… we will consider the problem of dynamically scoped, or "fluid", variables. These do not exist in ALGOL, but are typical of many LISP implementations, ECL, and APL. We will see that fluid variables may be modelled in more than one way, and that one of these is closely related to continuation-passing.
Gerald J. Sussman, Guy L. Steele Jr.. Scheme: An Interpreter for Extended Lambda Calculus. 维基文库. 1975 (英文). It is always possible, ……to perform any calculation in this way: rather than reducing to its value, it reduces to an application of a continuation to its value (cf. [Fischer]). That is, in this continuation-passing programming style, a function always "returns" its result by "sending" it to another function.
Gerald J. Sussman, Guy L. Steele Jr.The Revised Report on SCHEME: A Dialect of LISP. 1978. Non-atomic forms are divided by the evaluator into two classes: combinations and "magic (special) forms". …… Magic forms are recognized by then presence of a "magic (reserved) word" in the car position of the form. All non-atomic forms which are not magic forms are considered to be combinations. The system has a small initial set of magic words; there is also a mechanism for creating new ones {Note FUNCALL is a Pain}. A combination is considered to be a list of subforms. These subforms are all evaluated. The first value mub be a procedure; it is applied to the other values to get the value of the combination. There are four important points here: (1) the procedure Position is always evaluated just like any other position. (This is why the primitive operators are the values of global identifiers.) (2) the procedure is never "re-evaluated"; if the first subform fails to evaluate to a applicable procedure, it is an error. Thus, unlike most LISP systems, SCHEME always evaluates the first subform of a combination exactly once. (3) the arguments are all completely evaluated before the procedure is applied; that is, SCHEME, like most LISP systems, is an applicative-order language. Many SCHEME programs exploit this fact. (4) the argument forms (and procedure form) may in principle be evaluated in any order. This is unlike the usual LISP left-to-right order. (All SCHEME interpreters implemented so far have in fact performed left-to-right evaluation, but we do not wish programs to depend on this fact. Indeed, there are some reasons why a clever interpreter might want to evaluate them right-to-left, e.g. to get things on a stack in the correct order.)
Gerald J. Sussman, Guy L. Steele Jr.. Scheme: An Interpreter for Extended Lambda Calculus. 维基文库. 1975 (英文). BLOCK - This is like the MacLISP PROGN, but arranges to evaluate its last argument without an extra net control frame (explained later), so that the last argument may involved in an iteration. Note that in SCHEME, unlike MacLISP, the body of a LAMBDA expression is not an implicit PROGN. Gerald J. Sussman, Guy L. Steele Jr.. Lambda: The Ultimate Imperative. 维基文库. 1976 (英文). At this point we are almost in a position to model the most general form of compound statement. In LISP, this is called the "PROG feature". In addition to statement sequencing and GO TO statements, one can return a value from a PROG by using the RETURN statement. Let us first consider the simplest compound statement, which in SCHEME we call BLOCK. Recall that (BLOCK S1 S2) is defined to be ((LAMBDA (DUMMY) S2) S1) Notice that this not only performs S1 before S2, but also returns the value of S2. Furthermore, we defined (BLOCK S1 S2 ... Sn) so that it returns the value of Sn. Thus BLOCK may be used as a compound expression, and models a LISP PROGN, which is a PROG with no GO TO statements and an implicit RETURN of the last "statement" (really an expression). Gerald J. Sussman, Guy L. Steele Jr.The Revised Report on SCHEME: A Dialect of LISP. 1978. (BLOCK x1 x2 ... xn) (BLOCK x) → x (BLOCK x . r) → ((LAMBDA (A B) (B)) x (LAMBDA () (BLOCK . r))) BLOCK sequentially evaluates the subforms xi from left to right. For example: (BLOCK (ASET' X 43) (PRINT X) (+ X 1)) returns 44 after setting x to 43 and then printing it {Note BLOCK Exploits Applicative Order}. (LET ((v1 x1) (v2 x2) ... (vn xn)) . body) → ((LAMBDA (v1 v2 ... vn) (BLOCK . body)) x1 x2 ... xn) LET provides a convenient syntax for binding several variables to corresponding quantities. It allows the forms for the quantities to appear textually adjacent to their corresponding variables. Notice that the variables are all bound simultaneously, not sequentially, and that the initialization form xi may be evaluated in any order. For convenience, LET also supplies a BLOCK around the forms constituting its body.
Gerald J. Sussman, Guy L. Steele Jr.The Revised Report on SCHEME: A Dialect of LISP. 1978 [2021-11-07]. (原始内容存档于2022-04-06). Another difference from LISP is that SCHEME is implemented in such a way that tail-recursions execute without net growth of the interpreter stack. The effect of this is that a procedure call behaves like a GOTO and thus procedure calls can be used to implement iterations, as in PLASMA.
Gerald J. Sussman, Guy L. Steele Jr.. Scheme: An Interpreter for Extended Lambda Calculus. 维基文库. 1975 (英文). CATCH - This is the "escape operator" which gives the user a handle on the control structure of the interpreter. The expression: (CATCH <identifier> <expression>) evaluates <expression> in an environment where <identifier> is bound to a continuation which is "just about to return from the CATCH"; that is, if the continuation is called as a function of one argument, then control proceeds as if the CATCH expression had returned with the supplied (evaluated) argument as its value. …… As another example, we can define a THROW function, which may then be used with CATCH much as they are in LISP: (DEFINE THROW (LAMBDA (TAG RESULT) (TAG RESULT)))
Gerald J. Sussman, Guy L. Steele Jr.The Revised Report on SCHEME: A Dialect of LISP. 1978 [2021-11-07]. (原始内容存档于2022-04-06). Syntactic Extensions - SCHEME has a syntactic extension mechanism which provides a way to define an identifier to be a magic word, and to associate a function with that word. The function accepts the magic form as an argument, and produces a new form; this new form is then evaluated in place of the original (magic) form. This is precisely the same as the MacLISP macro facility.
Kohlbecker, E.; Friedman, D. P.; Felleisen, M.; Duba, B. Hygienic Macro Expansion(PDF). ACM conference on LISP and functional programming. 1986 [2021-11-10]. (原始内容(PDF)存档于2017-08-29). In most current Lisp systems the expander's task is confined to the process of finding syntactic extensions and replacing them by their expansions. This implies, in particular, the each macro function is responsible for the integrity of the program. For Lisp systems (and other languages with similar macro facilities) this means specifically that variable binding must not be corrupted. This, however, is not as simple a task as it sounds. …… The real danger of these erroneous macros is that they are treacherous. They work in all cases but one: when the user - or some other macro writer - inadvertently picks the wrong identifier name. Various techniques have been proposed to circumvent this capturing problem, but they rely on the individual macro writer. if even one of many macro writers is negligent, the macro system becomes unsafe. We claim that the task of safely renaming macro-generated identifier is mechanical. It is essentially an α-conversion, which is knowledgeable about the origin of the identifiers. For these reasons we propose a change to the navïe macro expansion algorithm which automatically maintains hygienic conditions during expansion time. Kohlbecker, E; Wand, M. Macro-by-example: Deriving syntactic transformations from their specifications(PDF). Symposium on Principles of Programming Languages. 1987 [2021-11-10]. (原始内容(PDF)存档于2022-04-12).
Gerald J. Sussman, Guy L. Steele Jr.The Revised Report on SCHEME: A Dialect of LISP. 1978 [2021-11-07]. (原始内容存档于2022-04-06). (ENCLOSE fnrep envrep) ENCLOSE takes two s-expressions, one representing the code for a procedure, and the other representing the (lexical) environment in which is to run. ENCLOSE returns a (closed) procedure which can be invoked. The representation of the code is the standard s-expression description (a lambda-expression). the representation of the environment is an association list (a-list) of the traditional kind: ((var1 . value1) (var2 . value2) ...) NIL represents the global lexical environment. …… The EVALUATE primitive described in [SCHEME] has been removed from the language. We discovered (the hard way) that the straightforward implementation of EVALUATE (evaluate the given expression in the current environment) destroys referential transparency(英语:referential transparency). We then altered it to evaluate the expression in the top-level environment, but were still disturbed by the extent to which one is tied to a particular representation of a procedure to be executed.