Scheme

Scheme是一种函数式编程语言，是Lisp的两种主要方言之一，不同于与之并列的Common Lisp，Scheme遵循哲学，以一个小型语言核心作为标准，加上各种强力语言工具（语法糖）来扩展语言本身。Scheme是第一個使用靜態作用域的Lisp方言，也是第一个引入头等续体和“干净宏”的编程语言。

简介
在1975年，麻省理工學院的傑拉德·傑伊·薩斯曼與蓋伊·史提爾二世，開發出了Scheme語言最初版本，隨後兩人通過發表「λ論文集」而不斷對它進行完善和推廣。Scheme與λ演算關係十分密切，故將小寫字母「λ」用作標誌。

麻省理工學院與其他一些院校，曾采用Scheme教授计算机科学入門課程。著名的入門教材《-{zh-hans:计算机程序的构造和解释;zh-hant:電腦程式的構造和解釋}-》（SICP），利用Scheme來詮釋程序設計。Scheme有眾多實現可視為一個主要優勢，然而不同實現之間的差異成為了它的一個劣勢，Scheme掌控委员会声称，它是“世上最不可移植的编程语言”，并且是一个“编程语言家族”而非一个单一的语言。

歷史

起源

Scheme起源於1958年由約翰·麥卡錫提出的Lisp語言。麥卡錫通過Lisp證明了，經由幾個簡單的算子，與用作匿名函數的借鑒自阿隆佐·邱奇的λ表示法，就可以構建出圖靈完備的系統。麥卡錫提出的S-表达式，可以将程序与数据用相同的结构存储，这被称为同像性。Scheme的語法即來自S-表达式，這一特性使得在Scheme中實現自循環直譯器變得非常簡單。

在1973年，麻省理工學院的提出的一種叫做演員模型的計算模型，并用Lisp开发当时叫做-73的新语言来实现它 “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
    (message M
        [#continuation C]))
The transmission (%T M%) is an abbreviation for the above where C is defaulted to be the caller. If target T is the following:
(receive
    (message the-body
        [#continuation =the-continuation])
    the-body)
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. ”。傑拉德·薩斯曼與蓋伊·史提爾为了理解演员模型，決定在Maclisp工作环境中實現一個微型Lisp解释器，并接着增加创建演员和发送消息的机制。兩人很快發現了演員模型與λ演算之間的相似性，所謂「演員」就是彼得·兰丁提出的閉包，而在1970年已将它介入Lisp用来解决；故而實現演員的關鍵，是將詞法作用域介入到Lisp中。

在1975年，基於对λ演算表达能力的理解，傑拉德·薩斯曼與蓋伊·史提爾開發出了新型Lisp解释器，并将在其上完成的微缩版的演员实现命名為「Schemer」，後因作業系統文件名的字符數限制而改為Scheme。儘管Hewitt指出了Scheme不能表达特定类型的原语演员，Scheme解释器本身采用的簡約的語法和语义，很快贏得廣泛接受。

在1978年，蓋伊·史提爾二世和傑拉德·傑伊·薩斯曼发表了《修订的Scheme报告：一种LISP方言》，从而完善了Scheme的功能，并正式将其确立为Lisp的一种主要方言。在1998年，二人在总结Scheme历史时指出，簡單而強大的λ演算，使得Scheme最終得以實現極度的精簡化。

λ論文集
「λ論文集」是傑拉德·薩斯曼與蓋伊·史提爾撰寫的關於Scheme的一系列論文，最早作為麻省理工學院的內部備忘錄發表。通常認定λ論文集包括：

 1975年: Scheme: An Interpreter for Extended Lambda Calculus.
 1976年: Lambda: The Ultimate Imperative.
 1976年: Lambda: The Ultimate Declarative.
 1977年: Debunking the 'Expensive Procedure Call' Myth, or, Procedure Call Implementations Considered Harmful, or, Lambda: The Ultimate GOTO.
 1978年: The Art of the Interpreter or, the Modularity Complex (Parts Zero, One, and Two).
 1978年: RABBIT: A Compiler for SCHEME.
 1979年: Design of LISP-based Processors, or SCHEME: A Dialect of LISP, or Finite Memories Considered Harmful, or LAMBDA: The Ultimate Opcode.
 1980年: Compiler Optimization Based on Viewing LAMBDA as RENAME + GOTO. AI: An MIT Perspective.
 1980年: Design of a Lisp-based Processor. CACM. 23. 11.

標準化
Scheme的業界標準，是由專門的掌控委員會發表的《第n次修訂的演算法語言Scheme報告》（Revisedn Report on the Algorithmic Language Scheme），这种标题形式参照了ALGOL 60标准文档的标题。Scheme报告中的形式语法描述采用了具有星号和加号扩展的巴科斯范式。Scheme报告中的形式语义描述所采用的概念和表示法，出自于在1977年的著作《指称语义：Scott-Strachey编程语言理论途径》。Scheme曾經由IEEE標準化為IEEE 1178–1990，它于2019年11月07日停用。

1998年通过的R5RS是現在被普遍接受的標準。2007年通過的R6RS，做出了很大的變動，Scheme社區中有使用者指責它在堆積華而不實的功能。Scheme掌控委員會決定在R7RS中，將Scheme分化為兩個獨立而兼容的語言：一個是2013年通過的R7RS-small，它為教學場合提供对IEEE/R5RS標準的及时更新，R7RS-small目前已经有了一些实现支持；而另一個是作为标准库合集的R7RS-Large，它包含了各种提议被接纳并进入冻结状态的（SRFI），以此為實際編程場合提供对R6RS標準的继续完善，现已发表了有关数据结构部份的R7RS-Large过渡草案红色版。

命名法
在正式场合比如Scheme标准的命名法中，提及一个λ表达式或原始过程时，偏好使用词语“过程”而非“函数”。在一般使用中，词语“过程”和“函数”是可互换使用的。过程应用有时被正式称呼为“组合”（combination）。

同其他Lisp一样，在Scheme中使用术语“”，来提及没有实际参数的过程。术语“适当”（proper）尾递归，称谓所有Scheme实现的一个性质，它们都进行尾递归优化，从而支持无限数目的活跃尾递归。

基础特征
Scheme大体上是一個函數式程式語言，並支援其他编程范型。它同其他Lisp编程语言家族语言共享了很多特征。它的非常简单的語法基於Lisp的S-表达式：即圆括号包围的列表，其中的前缀是算符，而随后那些的是实际参数。故而Scheme程序由嵌套的列表的序列构成。列表也是Scheme中的主要数据结构，这导致了在源代码和数据格式之间的紧密等价性，即同像性。Scheme程序可以轻易的动态建立和求值Scheme代码片段。

Scheme與其他Lisp方言的列表，都是基於最基礎的數據結構有序對（pair）。Scheme从它的Lisp先辈继承了一组丰富的列表处理原始运算，比如cons、和。Scheme的變數都使用動態強型別系統，Scheme中的过程都是頭等物件，即头等函数，因此过程可以作为值赋值给变量，或作为实际参数传递给过程。

本章主要集中于语言的创新特征，它们使得Scheme区别于其他Lisp方言。除非专门指出，这里描述的特征都有关于R5RS标准。在本章例子中使用“===> 结果”表示法，来指示求值在紧前行上的表达式的结果。这同于R5RS中使用的约定。本章节描述的特征使得Scheme不同于在它之前的其他编程语言。Scheme的这些方面强烈的影响了Scheme语言的所有产品，并共通于始自1975年最初的λ论文，特别是自从R2RS以来，所有版本的Scheme编程语言。

極簡主義
Scheme的簡約性，使它成為具備同級別功能的程式語言中最易於實現的語言，这得益于使用λ演算来从更原始的形式导出多数的语言语法。例如在R5RS标准中，定义了23个基于S-表达式的语法构造，其中14个被归类为派生形式或库形式，它们可以被写为涉及原则上为lambdad的更基础形式的宏。正如R5RS（§3.1）所说：“最基础的变量绑定构造是lambda表达式，因为所有其他的变量绑定构造，都可以依据lambda表达式来做出解释”。
 基础形式：define、lambda、quote、if、define-syntax、let-syntax、letrec-syntax、syntax-rules、set!
 派生形式：do、let、let*、letrec、cond、case、and、or、begin、命名let、delay、unquote、unquote-splicing、quasiquote
下面是一个宏的例子，它将let实现为使用lambda来进行变量绑定的一个表达式：
(define-syntax let
  (syntax-rules ()
    ((let ((name val) ...) body1 body2 ...)
     ((lambda (name ...) body1 body2 ...) val ...))))

这里在define-syntax之后出现的let是这个宏的“语法关键字”或简称“关键字”，随后是变换规定：syntax-rules之后的空括号中可添入作为辅助关键字的叫做“文字”的标识符，称为“模式”，模式起始于关键字let，name、val、body1和body2称为“模式变量”，...是称为“省略号”的标识符，它表示所跟随的子模式可以匹配零或多个输入中的元素；而称为“模板”。使用上述定义的let，一个Scheme实现可以将，重写为，这将实现任务缩减为编码过程实例化的工作。

词法作用域

不像更早期的Lisp比如Maclisp，Scheme像多数现代语言一样，采用了词法作用域：在一个程序单元中所有可能的变量绑定，都可以通过阅读这个程序单元来分析出来，而不需要考虑到它可能在其中被调用的那些上下文。这对比于动态作用域，它是早期Lisp方言的特征，因为在当时的编译器和解释器中，用来实现词法作用域算法的原始的文字替换方法，关联着处理代价。在动态作用域的Lisp中，对一个过程内的自由变量的引用，依赖于这个调用的上下文，完全有可能引用到这个过程外部的相当不同的绑定。

下面是男人抑或男孩测试的例子：
(define (A k x1 x2 x3 x4 x5)
  (define (B)
    (set! k (- k 1))
    (A k B x1 x2 x3 x4))
  (if (<= k 0)
    (+ (x4) (x5))
    (B)))

(A 10 (lambda () 1) (lambda () -1) (lambda () -1) (lambda () 1) (lambda () 0))

λ演算

邱奇的λ演算的数学表示法，启发了Lisp使用lambda作为介入一个过程的关键字，并影响了Lisp中涉及到使用高阶函数的函数式编程技术的发展。但是早期的Lisp由于对自由变量的处理方式，而不适合表达λ演算。

一个正式的λ系统，拥有一些公理和一个完备的推理规则。这有助于使用数理逻辑和工具来进行分析。在这个系统中，演算可以被看作直接的演绎。λ演算的语法，来自用x, y, z, ...、圆括号、空格、点号和符号λ形成的递归表达式。λ演算的功能包括：首先，充当强力的数理逻辑的起点。其次，它可以缩减编程者在考虑实现细节上的要求，因为它可以用于模拟机器求值。最后，λ演算建立了一个坚实的元理论。

在Scheme中，lambda關鍵字被用於定義匿名过程，并且使用define基础形式来定义命名过程，即将一个lambda过程绑定到指名的全局变量。在Scheme中，與，在語法上是等同的。例如有序對可以表示為：
(define (cons x y)
  (lambda (m) (m x y)))

(define (car z)
  (z (lambda (p q) p)))

(define (cdr z)
  (z (lambda (p q) q)))
這樣定義出來的cons、car和cdr，滿足恆等式(car (cons x y))等於x，和(cdr (cons x y))等於y。甚至递归也可以表示为利用λ演算的Y组合子。

词法作用域的介入，通过在某些形式的λ表示法，和在工作编程语言中它们的实际表达之间做出等价，解决了早期Lisp的有关问题。Sussman和Steele展示了，通过将λ表达式用作“控制结构和环境修改器”，而非简单的过程实例化，可以用这个新语言优雅的导出其他编程语言包括ALGOL和Fortran的，所有指令式和声明式语义，和其他Lisp的动态作用域。他们在第一篇λ论文中，与对Scheme的首次描述一起，介入了，并在后续的论文中，他们推进演示了在这种实际使用中体现出的λ演算的原生能力。

过程应用中的求值次序
Scheme采用了传值调用，但不同于多数Lisp规定了过程实际参数的求值次序，Scheme不规定求值次序。对比于其他Lisp，Scheme表达式在算符位置（第一个项目）上可以是一个表达式，只要在算符位置上的表达式的结果是一个过程，这种表示就是完全合法的。

在Scheme中，在算符和实际参数位置上的这些表达式的求值次序，可以在逐个调用的基础上由实现来选择，而唯一的约束是：“运算符和运算数表达式的任何并发求值的效果，被约束为一致于某种顺序次序的求值。”（R5RS sec. 4.1.3）
(let
  ((ev (lambda (n)
    (display "Evaluating ")
    (display (if (procedure? n) "procedure" n))
    (newline) 
    n)))
  ((ev +) (ev 1) (ev 2)))
ev是一个过程，它描述传递给它的实际参数，接着返回这个实际参数的值。在调用过程+来加1和2之中，表达式(ev +)、(ev 1)和(ev 2)可以按任何次序求值，只要效果上它们不像是并行求值的。因此在上述例子被执行的时候，标准Scheme可以按任何次序来显示前三行：
Evaluating procedure
Evaluating 1
Evaluating 2
3
然而一行的文本不可以同另一行的文本产生交错，因为这会违反顺序求值约束。

塊結構
Scheme的代碼塊結構，不同于早先Lisp的語言構造。自从R2RS开始，Scheme中的块，是通过三个“绑定构造”来实现的：、let*和letrec。例如，下列构造建立一个块，其中叫做var的符号被绑定到数10：
(define var "goose")
;; 这里任何到var的引用都会被绑定到"goose"
(let ((var 10))
  ;; 语句走到这里时，任何到var的引用都会绑定到10
  )
;; 这里任何到var的引用都会绑定到"goose"

依据编程者的需要，块可以来建立任意复杂的块结构。使用块结构来建立局部绑定，减轻了不然可能发生的。

let的变体之一let*，允许绑定引用在前面相同构造中定义的变量，例如：

(let* 
  ((var1 10)
   (var2 (+ var1 12)))
  ;; 但是var1的定义不可以引用var2
  )

let的另一个变体letrec，被设计用来确使互递归过程可绑定彼此。

下面的例子是侯世达：
;; 将侯世达雌雄序列计算为有序对的列表
(define (hofstadter-male-female n)
  (letrec
    ((female (lambda (n)
	  (if (= n 0)
	    1
		(- n (male (female (- n 1)))))))
	 (male (lambda (n)
	  (if (= n 0)
		0
		(- n (female (male (- n 1))))))))
    (let loop ((i 0))
      (if (> i n)
	    '()
	    (cons (cons (female i) (male i))
		  (loop (+ i 1)))))))

(hofstadter-male-female 8)
===> ((1 . 0) (1 . 0) (2 . 1) (2 . 2) (3 . 2) (3 . 3) (4 . 4) (5 . 4) (5 . 5))
在一个单一的letrec中的所有过程，可以通过名字引用其他的过程，还有在相同的letrec中此前定义的变量的值，但是它们不可以引用在相同的letrec中此后定义的变量的值。

let的变体“命名let”形式，在let关键字之后有一个标识符。它将这些let变量绑定到一个过程的实际参数，这个过程的名字由这个标识符给出，它的过程体是let形式的主体。这个过程体可以通过调用这个过程达成想要的重复。命名let被广泛用于实现迭代。

一个简单的计数器例子:
(let loop ((n 1))
  (if (> n 10)
    '()
    (cons n
	  (loop (+ n 1)))))
===> (1 2 3 4 5 6 7 8 9 10)
就像Scheme中的任何过程一样，在命名let中建立的这个过程是头等对象。

适当尾递归

Scheme拥有一个迭代构造do，但是在Scheme中更推崇的，是使用尾递归来表达迭代。遵循标准的Scheme实现被要求优化尾递归，从而支持无限数量的活跃尾递归（R5RS sec. 3.5），这个性质被Scheme报告描述为“适当”（proper）尾递归，它使得Scheme编程者可以安全的使用递归结构来书写迭代算法，这在很多时候是更符合直觉的。尾递归过程和命名let形式，提供对使用尾递归的迭代的支持。

;; 建造从0到9的平方的列表：
;; 注意：loop简单的就是用作标签的一个任意符号。任何符号都行。
(define (list-of-squares n)
  (let loop ((i n) (res '()))
    (if (< i 0)
      res
      (loop (- i 1) (cons (* i i) res)))))

(list-of-squares 9)
===> (0 1 4 9 16 25 36 49 64 81)

头等续体

在Scheme中续体是头等对象。自从R2RS开始，Scheme提供了过程，简写为call/cc，它捕获当前续体，将其包装成逃脱（escape）过程，再绑定到编程者提供的一个过程的唯一形式参数上。如果逃脱过程在后来某个时候被调用，它会放弃在后来时候正生效的任何续体，转而使用在创建这个逃脱过程之时生效的那个续体。逃脱过程与原本调用call/cc的续体，接受相同数目的实际参数；除了用call-with-values创建的续体，所有续体恰好接受一个值（R5RS sec. 6.4）。

头等续体使得编程者能够建立非局部控制结构比如迭代器、协程和回溯。续体可以被用来模拟指令式编程语言中return语句的行为。下列函数find-first接受函数func和列表lst，返回lst中的第一个元素x，它使得(func x)返回真。

(define (find-first func lst)
  (call/cc (lambda (return)
    (for-each (lambda (x)
      (if (func x)
        (return x)))
      lst)
    #f)))

(find-first integer? '(1/2 3/4 5.6 7 8/9 10 11))
===> 7
(find-first zero? '(1 2 3 4))
===> #f

David Madore在1999年提出的阴阳谜题David Madore, "call/cc mind-boggler" ，展示了Scheme可以将续体当作头等对象处理，绑定它们到变量，和把它们作为给过程的实际参数来传递Yin Wang, "Understanding the Yin-Yang Puzzle"。

统一的命名空间
对比于Common Lisp，在Scheme中所有的数据和过程共享一个共同的命名空间，而Common Lisp中有函数和数据分离的命名空间，使得一个函数和一个变量可以有相同的名字，并且将一个函数作为值引用时要求特殊的表示法。这有时叫做“Lisp1与Lisp2”差异，二者分别称谓Scheme的统一的命名空间，和Common Lisp的分立的命名空间。

在Scheme中， 可以使用操纵和绑定数据的原始运算来绑定过程。没有等价于Common Lisp的defun和#'的原始运算。
;; 变量绑定到一个数：
(define f 10)
f
===> 10
;; 变化（改变绑定值）
(set! f (+ f f 6))
f
===> 26
;; 将一个过程赋值给相同的变量：
(set! f (lambda (n) (+ n 12)))
(f 6)
===> 18
;; 将一个表达式的结果赋值给相同的变量：
(set! f (f 1))
f
===> 13
;; 函数式编程：
(apply + '(1 2 3 4 5 6))
===> 21
(set! f (lambda (n) (+ n 100)))
(map f '(1 2 3))
===> (101 102 103)

实现标准
本章归档多年来做出的给与Scheme特定特征的设计决定，它们不是最初设计的直接产物。

注释

直到R5RS标准，在Scheme中的标准注释是分号，它使得这行余下部份对Scheme不可见。许多实现支持可替代的约定，允许注释扩展为多于一行，而R6RS标准允许其中的两种：一个完整的S-表达式，可以通过前导#; 而变成一个注释（介入于SRFI 62），和通过用#|和|#围绕文本，产生的“多行注释”或“块注释”。

在布尔表达式中非布尔值的处理
在多数Lisp方言包括Common Lisp中，布尔表达式中的值NIL按照惯例被求值为值假。在Scheme中，自从1991年的IEEE标准，除了#f的所有的值，包括在Scheme中写为'()的NIL的等价者，在布尔表达式中求值为值真（R5RS sec. 6.3.1）。

在多数Lisp中表示布尔值真的常量是T，而在Scheme中它是#t。

干净宏

Scheme的语法可以轻易的通过宏系统来扩展。R5RS标准介入了强力的干净宏系统，它允许编程者使用一种简单的模式匹配子语言，向语言增加的新的语法构造（R5RS sec 4.3）。在此前的R4RS标准中，干净宏系统已经作为“高级”系统，和“低级”宏系统一起被编排入标准的附录中，二者都被当作对Scheme的扩展而非语言的本质部份。

干净宏的实现，也叫做syntax-rules, 被要求遵守语言的其余部份的词法作用域。这是通过针对宏展开的特殊命名和作用域规则来确保的，从而避免在其他编程语言的宏系统中可能出现的常见编程错误。R6RS规定了更加复杂的变换系统syntax-case，它已经作为对R5RS Scheme的一个语言扩展而能够获得到有一段时间了。例如：
;; 定义一个宏来实现“if”的具有多个表达式的变体
;; 有真分支而无假分支
(define-syntax when
  (syntax-rules ()
    ((when pred exp exps ...)
     (if pred (begin exp exps ...)))))

宏和过程的调用看起来非常相似，二者都是S-表达式，但是它们被不同的对待。在编译器遇到程序中的一个S-表达式的时候，它首先查看这个符号是否被定义为在当前词法范围内的语法关键字。如果是这样，它接着尝试展开这个宏，将在这个S-表达式尾部的项目当作实际参数，而不用编译代码来求值它们，递归的重复这个处理过程直到没有余留的宏调用。如果它不是一个语法关键字，编译器编译代码来求值在这个S-表达式尾部的实际参数，并接着求值在这个S-表达式头部的符号所表示的变量，把它作为过程来调用，并把最终的尾部表达式作为实际参数传递给它。

多数Scheme实现还提供额外的宏系统。其中最流行是语法闭包、显式重命名宏和define-macro，它是类似于Common Lisp中提供的defmacro系统的非干净宏。

不能指定一个宏是否为干净的，是宏系统的一个缺点。可作为替代的展开模型比如作用域集合，提供一种潜在的解决方案。

延迟求值

自从R2RS开始，Scheme通过delay形式和过程force支持延迟求值。

(define a 10)
(define eval-aplus2 (delay (+ a 2)))
(set! a 20)
(force eval-aplus2)
===> 22
(define eval-aplus50 (delay (+ a 50)))
(let ((a 8))
  (force eval-aplus50))
===> 70
(set! a 100)
(force eval-aplus2)
===> 22

delay原始运算，产生叫做promise的一个对象，它在将来的某个时间点上被询问从而递交结果值，promise的原本定义的文字内容会被留存，并且在第一次使用force之后它的值也会被留存。promise永远只求值一次。它们可以被用来实现高级的惰性求值构造比如串流。

在R5RS中，给出了delay和force的推荐实现，将promise实现为没有实际参数的一个过程（），并使用记忆化来确保它永远只求值一次，与调用force的次数无关（R5RS sec. 6.4）。在R6RS标准中，它们不再是原始运算，转而作为R5RS兼容库的一部份提供（rnrs r5rs (6)）。

SRFI 41确使表达有限和无限序列能够具有非凡的经济性。例如，这是使用在SRFI 41中的函数定义的一个斐波那契数列：

;; 定义斐波那契序列
(define fibs
  (stream-cons 0
    (stream-cons 1
      (stream-map +
        fibs
        (stream-cdr fibs)))))

;; 计算这个序列的第一百个数
(stream-ref fibs 99)
===>  218922995834555169026

eval及其运行环境
R5RS标准之前，在其他Lisp中无处不在的eval过程，在Scheme中没有等价者。在第一篇λ论文中，曾经将evaluate描述为“类似于LISP函数EVAL”，但在具有词法作用域的Scheme中，对这个表达式在哪个环境中求值存在困惑。例如，不明确求值下列表达式的结果应当是5还是6：
(let ((name '+))
  (let ((+ *))
    (evaluate (list name 2 3))))
在求值实际参数name的时候，在外层环境中找到了它的定义；在求值结果表达式(+ 2 3)的时候，如果在外层环境中求值，结果是两个运算数的总和；如果在内层环境中求值，这里符号+已经被绑定到过程*的值，结果是两个运算数的乘积。为此在1978年的最初修订报告中，将evaluate替代为enclose，它接受分别为代码和运行环境的两个实际参数。由于各种技术和实际原因，第二次、第三次和第四次修订报告省略了任何eval的等价者。

R5RS通过规定三个返回环境的过程，并提供接受一个S-表达式和一个环境的过程eval，它在提供的这个环境内求值这个表达式（R5RS sec. 6.5），解决了这个困惑。R6RS通过提供叫做environment的一个过程进行了扩展，编程者通过它可以精确的指定将哪个对象导入到求值环境之内。

通过现代Scheme（通常兼容于R5RS）来求值表达式expr，你需要定义如下这样的一个函数evaluate：

(define (evaluate expr)
  (eval expr (interaction-environment)))

interaction-environment是解释器的全局环境。

数值塔

Scheme规定了数值数据类型的相较完全的集合，包括复数和有理数类型，这在Scheme中叫做“数值塔”（R5RS sec. 6.2）。标准对它们作抽象处理，而不委以任何特定的内部表示。

数可以有精确性品质。一个精确数只能从涉及其他精确数的精确运算产生，不精确是传染的。标准规定任何两个实现对产生精确数的所有运算都必须产生等价的结果。

R5RS标准规定了过程exact->inexact和inexact->exact，它们可以用于改变一个数的精确性。inexact->exact产生“在数值上最接近实际参数的精确数”。exact->inexact产生“在数值上最接近实际参数的不精确数”。R6RS标准在主报告中省略了这些过程，而是在标准库中将它们规定为R5RS兼容过程（rnrs r5rs (6)）。

在R5RS标准中，不要求Scheme实现去实现整个数值塔，而是它们必须实现“符合实现用途和Scheme语言精神二者的连贯子集”（R5RS sec. 6.2.3）。新的R6RS标准要求实现整个数值塔，并且“精确整数对象和精确有理数对象实际上有无限的大小和精度，并且对于实现特定过程...所以它们在得到精确实际参数时总是返回精确结果”（R6RS sec. 3.4, sec. 11.7.1）。

在支持精确有理复数的实现中的精确算术例子： 
;; 三个有理实数和两个有理复数的总和
(define x (+ 1/3 1/4 -1/5 -1/3i 405/50+2/3i))
x
===> 509/60+1/3i
;; 检查精确性
(exact? x)
===> #t

在既不支持精确有理数也不支持复数却接受有理数表示法的实数的实现中相同算术的例子：
;; 四个有理实数的总和
(define xr (+ 1/3 1/4 -1/5 405/50))
;; 两个有理实数的总和
(define xi (+ -1/3 2/3))
xr
===> 8.48333333333333
xi
===> 0.333333333333333
;; 检查精确性
(exact? xr)
===> #f
(exact? xi)
===> #f

二者实现都符合R5RS标准，但是第二个实现不符合R6RS，因为它没有实现完整的数值塔。

原始数据类型不相交
在Scheme中原始数据类型是不相交的。对于任何Scheme对象，下列谓词中只有一个可以为真：boolean?、pair?、symbol?、number?、char?、>string?、vector?、port?和procedure?（R5RS sec 3.2）。

作为对比，在数值数据类型之内，对数值值是可以有交叠的。例如，一个整数值可以同时满足如下谓词：integer?、rational?、real?、complex?和number?（R5RS sec 6.2）。

等价谓词

Scheme在任意对象之间有三种不同类型的等价，它们通过三种不同的“等价谓词”来指示，即测试等式的关系算符eq?、eqv?和equal?：
  eq?求值为#f，除非它的实际参数表示在内存中相同的对象；
 eqv?大体上同于eq?，但是特殊处理原始对象（比如字符和数），使得表示相同值的数eqv?为真，即使它们不引用相同的对象；
 equal?比较数据结构比如列表、向量和字符串，来确定它们是否有全等的结构并且其内容eqv?为真（R5RS sec. 6.1）。

在Scheme中还存在依赖于类型的等价运算：string=?和string-ci=?比较两个字符串（后者进行不依赖大小写比较）；char=?和char-ci=?比较字符；=比较数。

输入/输出
Scheme的输入和输出基于了“端口”数据类型（R5RS sec 6.6）。R5RS定义了两个缺省端口，可通过过程current-input-port和current-output-port来访问，它们对应于Unix概念的标准输入和标准输出。多数实现还提供current-error-port。标准通过标准过程比如with-input-from-file和with-output-to-file，支持输入和标准输出的重定向。多数实现提供有重定向能力的字符串端口，使用在SRFI 6中描述的过程，确使很多常规的输入-输出运算能在字符串缓冲区上进行而非在文件上。R6RS标准规定了更多复杂和有能力的端口过程和很多新的端口类型。

下面的例子是使用严格的R5RS Scheme书写的。

例子1，缺省输出到current-output-port：
(let ((hello0 (lambda() (display "Hello world") (newline))))
  (hello0))

例子2，类似例子1但对输出过程使用可选的端口参数的例子：
(let ((hello1 (lambda (p) (display "Hello world" p) (newline p))))
  (hello1 (current-output-port)))

类似例子1，但是输出被重定向到一个新建文件：
;; NB: with-output-to-file is an optional procedure in R5RS
(let ((hello0 (lambda () (display "Hello world") (newline))))
  (with-output-to-file "helloworldoutputfile" hello0))
类似例子2，但是具有显式的文件打开和端口关闭来发送输出到文件：
(let ((hello1 (lambda (p) (display "Hello world" p) (newline p)))
      (output-port (open-output-file "helloworldoutputfile")))
  (hello1 output-port)
  (close-output-port output-port))

类似例子2，但是使用call-with-output-file来发送输出到一个文件：
(let ((hello1 (lambda (p) (display "Hello world" p) (newline p))))
  (call-with-output-file "helloworldoutputfile" hello1))

对输入提供了类似的过程。R5RS Scheme提供了谓词input-port?和output-port?。对于字符输入和输出提供了write-char、read-char、peek-char和char-ready?。为了书写和阅读Scheme表达式，Scheme提供了read和write。在读运算时，如果输入端口到达了文件的结束处，则返回的结果是end-of-file对象，并且这可以使用谓词eof-object?来测试。

除了标准之外，SRFI 28定义了一个基本的格式化过程，类似Common Lisp的format并以此命名。

标准过程的重定义
在Scheme中，过程被绑定到变量。在R5RS中语言标准正式的授权编程者可以变更内建过程的变量绑定，在效果上重定义它们（R5RS "Language changes"）。例如，通过重定义+可以将它扩展为同接受数一样接受字符串：

(set! +
  (let ((original+ +))
    (lambda args
      (apply
        (if (or (null? args) (string? (car args)))
          string-append
          original+)
        args))))

(+ 1 2 3)
===> 6
(+ "1" "2" "3")
===> "123"

在R6RS中所有绑定，包括标准过程，都属于某个库，并且所有导出的绑定都是不可变的（R6RS sec 7.1）。因此，禁止通过变更进行标准过程的重定义。转而，可以在标准过程的名字下导入不同的过程，这在效果上类似于重定义。

标准形式和过程概述
Scheme语言正式定义于1998年的R5RS和2007年R6RS标准之中。它们描述了标准“形式”，即关键字和随同的语法，它们提供语言的控制结构，和执行通常任务的标准过程。

在标准Scheme中，从一个数据类型转换到另一个数据类型的过程，在它们的名字中包含字符串->，谓词结束于一个?，而改变已经分配了数据的值的过程结束于一个!。Scheme编程者通常跟从这些命名约定。

标准形式
本表格描述Scheme中的标准形式。某些形式出现在不止一行之中，因为它们不能被轻易的归类入语言中的一个单一功能。

在表格中标记了“L”的形式被归类为标准中的派生的“库”形式，并且在实践中通常被实现为使用了更基础形式的宏，这使得实现任务比在其他语言中要容易许多。
+ R5RS Scheme语言中的标准形式用途  形式 定义       绑定构造   条件求值   顺序求值   迭代      语法扩展   引述      赋值      延迟求值  

注意begin在R5RS中被定义为库语法，但是展开者需要知道它来完成分片功能。在R6RS中它不再是库语法。

标准过程

下面两个表格描述在R5RS Scheme中的标准过程。R6RS更加具有可扩展性而如此总结是不实际的。

某些过程出现在不止一行之中，因为它们不能轻易的分类入语言的一个单一功能。
+ R5RS Scheme语言中的标准过程用途  过程 构造      等价谓词   类型转换   数值      字符串    字符     向量     符号     有序对和列表   识别谓词   续体      环境      输入/输出   系统接口   延迟求值   函数式编程   布尔逻辑  

在其名字中包含-ci的字符串和字符过程，在它们的实际参数之间进行不依赖大小写的比较：相同字符的大写和小写版本被认为是相等的。

+ R5RS Scheme语言中的标准数值过程用途  过程 基本算术算符   有理数   近似     精确性   不等式   杂项谓词   极大和极小   三角    指数    复数    输入-输出   类型谓词   

接受多于二个实际参数的-和/在R5RS中被定义了但留作为可选的。

 Scheme实现要求 

由于Scheme的极简主义，很多常见过程和语法形式不是由标准定义的。 为了保持核心语言很小并促进扩展的标准化，Scheme社群拥有“Scheme实现要求”（SRFI）流程，通过对扩展提案的仔细研讨来定义扩展库。这提升了代码可移植性。很多SRFI被几乎所有Scheme实现支持。

在不同的实现中具有相当广泛支持的SRFI包括：

 0: 基于特征的条件展开构造
 1: 列表库
 4: 同质数值向量数据类型
 6: 基本字符串端口
 8: 接收、绑定到多个值
 9: 定义记录类型
 13: 字符串库
 14: 字符集库
 16: 可变元数过程的语法
 17: 广义set!
 18: 多线程支持
 19: 时间数据类型和过程
 25: 多维数组原语
 26: 不用柯里化的特殊化形式参数的表示法
 27: 随机数位的来源
 28: 基本格式化字符串
 29: 本地化
 30: 嵌套的多行注释
 31: 递归求值的特殊形式
 37: args-fold：程序实际参数处理器
 39: 形式参数对象
 41: 串流
 42: 及早推导式
 43: 向量库
 45: 表达迭代式惰性算法的原语
 60: 作为位元的整数
 61: 更一般性的cond子句
 66: 八位组向量
 67: 比较过程

實作
Scheme的精簡設計，使得程式語言設計人士與愛好者，特別鍾愛研究它，故而它有不斷湧現的新實作，而活躍開發的實作也在持續跟進語言標準更新。儘管Scheme有眾多實現是它的一個主要長處，但是由于Scheme没有库函数标准，造成了很多不可互通的實作互相競爭，试图使用Scheme编写既复杂又便于移植的程序，往往比较困难。R6RS和R7RS-Large，在核心语言之外制定了库函数标准，使得编译器开发者和贡献者可以实现Scheme的可移植库。

幾乎所有Scheme實作都有传承自Lisp的「讀取﹣求值﹣輸出循環」交互模式，一些Scheme實作亦可作為編譯器。很多用C语言及衍生语言寫成的軟體，都利用Scheme作為腳本語言，很多嵌入式系統語言即是基於Scheme。Chez Scheme等实现，可以將Scheme程式編譯為本機二進制碼。Gambit和Chicken等实现，可將Scheme程式編譯為C代码，还能编译成JVM字节码或者.Net字节码。

一些实现支持额外的特征。比如提供与Java类的集成，而Scheme到C编译器通常易于使用C写成的外部库，甚至允许将实际C代码嵌入到Scheme源代码中。

教科書

 《-{zh-hans:计算机程序的构造和解释;zh-hant:電腦程式的構造和解釋}-》（SICP），由Scheme創始人之一傑拉德·薩斯曼與哈尔·阿伯尔森編寫，它是最著名的使用Scheme語言的電腦科學教科書。書中用Scheme自身實現了Scheme的自循環直譯器，其簡單的語法可以幫助使用者理解Scheme的執行過程。
 《-{zh-hans:程序设计方法;zh-hant:程式設計方法}-》，由马蒂亚斯·费莱森等人編寫，它對SICP中的一些被認為過於艱澀的概念進行了改進。
 《》，由丹尼尔·福瑞得曼等人编写，它是以Scheme为基础的教科书。
 《简单的Scheme：介绍计算机科学》，由柏克萊加州大學資深講師布萊恩·哈維編寫，它是一本專為中學級別，無電腦科學基礎的學生編寫的入門書。

實際用處
很多著名的電腦科學院校都利用Scheme來教授入門級課程。以下為一些最為著名的教授Scheme的學校：

 麻省理工學院是Scheme與SICP的誕生地。直到2008年為止，麻省理工學院的入門課程6.001即是用Scheme來教授的。儘管現在Scheme已經不再被用於入門課程，麻省理工學院到目前為止還在教授SICP。
 柏克萊加州大學的入門課程61A到2010年為止利用Scheme與SICP教授入門課程，並利用Scheme來實作Logo，另一個基於Lisp的程式語言。自2011年起，61A改用Python來教授SICP。
 西北大學的入門課程CS2500利用Scheme來教授另一本著名的教材《-{zh-hans:程序设计方法;zh-hant:程式設計方法}-》。
 印第安那大學的入門課程C211利用Scheme來教授。
 耶魯大學
 萊斯大學
 香港科技大學
 北京大學
 項目在美國超過600所高中教授Scheme語言。
 滑铁卢大学数学系（包括计算机科学系）的入門課程CS115、CS116、CS135利用Scheme來教授。
 雲林科技大學

自由軟體影像處理程式GIMP利用Scheme為嵌入式脚本語言。GNOME中有到核心库的一个GNU的扩展語言Guile包装器。在2012年出现的Julia所采用的语法解析器，是用Scheme方言Femtolisp实现的。

 参见 
LISP
Racket

註釋

 延伸阅读 


 外部链接 

Awesome Scheme 
Bookmarklet that add Interactive Scheme REPL to any website 

Category:动态类型编程语言
Category:学术的编程语言
Category:LISP程式語言家族

Category:1975年建立的程式語言