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.