Recent from talks
Nothing was collected or created yet.
Scheme (programming language)
View on Wikipedia| Scheme | |
|---|---|
| Paradigms | Multi-paradigm: functional, imperative, meta |
| Family | Lisp |
| Designed by | Guy L. Steele Gerald Jay Sussman |
| First appeared | 1975 |
| Stable release | R7RS
/ 2013 |
| Typing discipline | Dynamic, latent, strong |
| Scope | Lexical |
| Filename extensions | .scm, .ss |
| Website | www |
| Major implementations | |
| Many (see Scheme implementations) | |
| Influenced by | |
| ALGOL, Lisp, MDL | |
| Influenced | |
| Clojure, Common Lisp, Dylan, EuLisp, Haskell, Hop, JavaScript, Julia, Lua, MultiLisp, Python, R, Racket, Ruby, Rust,[1] S, Scala, T | |
| |
Scheme is a dialect of the Lisp family of programming languages. Scheme was created during the 1970s at the MIT Computer Science and Artificial Intelligence Laboratory (MIT CSAIL) and released by its developers, Guy L. Steele and Gerald Jay Sussman, via a series of memos now known as the Lambda Papers. It was the first dialect of Lisp to choose lexical scope and the first to require implementations to perform tail-call optimization, giving stronger support for functional programming and associated techniques such as recursive algorithms. It was also one of the first programming languages to support first-class continuations. It had a significant influence on the effort that led to the development of Common Lisp.[2]
The Scheme language is standardized in the official Institute of Electrical and Electronics Engineers (IEEE) standard[3] and a de facto standard called the Revisedn Report on the Algorithmic Language Scheme (RnRS). A widely implemented standard is R5RS (1998).[4] The most recently ratified standard of Scheme is "R7RS-small" (2013).[5] The more expansive and modular R6RS was ratified in 2007.[6] Both trace their descent from R5RS; the timeline below reflects the chronological order of ratification.
History
[edit]Origins
[edit]Scheme started in the 1970s as an attempt to understand Carl Hewitt's Actor model, for which purpose Steele and Sussman wrote a "tiny Lisp interpreter" using Maclisp and then "added mechanisms for creating actors and sending messages".[7] Scheme was originally called "Schemer", in the tradition of other Lisp-derived languages such as Planner or Conniver. The current name resulted from the authors' use of the ITS operating system, which limited filenames to two components of at most six characters each. Currently, "Schemer" is commonly used to refer to a Scheme programmer.
R6RS
[edit]A new language standardization process began at the 2003 Scheme workshop, with the goal of producing an R6RS standard in 2006. This process broke with the earlier RnRS approach of unanimity.
R6RS features a standard module system, allowing a split between the core language and libraries. Several drafts of the R6RS specification were released, the final version being R5.97RS. A successful vote resulted in ratifying the new standard, announced on August 28, 2007.[6]
Currently the newest releases of various Scheme implementations[8] support the R6RS standard. There is a portable reference implementation of the proposed implicitly phased libraries for R6RS, called psyntax, which loads and bootstraps itself properly on various older Scheme implementations.[9]
A feature of R6RS is the record-type descriptor (RTD). When an RTD is created and used, the record type representation can show the memory layout. It also calculated object field bit mask and mutable Scheme object field bit masks, and helped the garbage collector know what to do with the fields without traversing the whole fields list that are saved in the RTD. RTD allows users to expand the basic RTD to create a new record system.[10]
R6RS introduces numerous significant changes to the language.[11] The source code is now specified in Unicode, and a large subset of Unicode characters may now appear in Scheme symbols and identifiers, and there are other minor changes to the lexical rules. Character data is also now specified in Unicode. Many standard procedures have been moved to the new standard libraries, which themselves form a large expansion of the standard, containing procedures and syntactic forms that were formerly not part of the standard. A new module system has been introduced, and systems for exception handling are now standardized. Syntax-rules has been replaced with a more expressive syntactic abstraction facility (syntax-case) which allows the use of all of Scheme at macro expansion time. Compliant implementations are now required to support Scheme's full numeric tower, and the semantics of numbers have been expanded, mainly in the direction of support for the IEEE 754 standard for floating point numerical representation.
R7RS
[edit]The R6RS standard has caused controversy because some see it as a departure from the minimalist philosophy.[12][13] In August 2009, the Scheme Steering Committee, which oversees the standardization process, announced its intention to recommend splitting Scheme into two languages: a large modern programming language for programmers; and a small version, a subset of the large version retaining the minimalism praised by educators and casual implementors.[14] Two working groups were created to work on these two new versions of Scheme. The Scheme Reports Process site has links to the working groups' charters, public discussions and issue tracking system.
The ninth draft of R7RS (small language) was made available on April 15, 2013.[15] A vote ratifying this draft closed on May 20, 2013,[16] and the final report has been available since August 6, 2013, describing "the 'small' language of that effort: therefore it cannot be considered in isolation as the successor to R6RS".[5]
| 1958 | 1960 | 1965 | 1970 | 1975 | 1980 | 1985 | 1990 | 1995 | 2000 | 2005 | 2010 | 2015 | 2020 | ||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| LISP 1, 1.5, LISP 2(abandoned) | |||||||||||||||
| Maclisp | |||||||||||||||
| Interlisp | |||||||||||||||
| MDL | |||||||||||||||
| Lisp Machine Lisp | |||||||||||||||
| Scheme | R5RS | R6RS | R7RS small | ||||||||||||
| NIL | |||||||||||||||
| ZIL (Zork Implementation Language) | |||||||||||||||
| Franz Lisp | |||||||||||||||
| muLisp | |||||||||||||||
| Common Lisp | ANSI standard | ||||||||||||||
| Le Lisp | |||||||||||||||
| MIT Scheme | |||||||||||||||
| XLISP | |||||||||||||||
| T | |||||||||||||||
| Chez Scheme | |||||||||||||||
| Emacs Lisp | |||||||||||||||
| AutoLISP | |||||||||||||||
| PicoLisp | |||||||||||||||
| Gambit | |||||||||||||||
| EuLisp | |||||||||||||||
| ISLISP | |||||||||||||||
| OpenLisp | |||||||||||||||
| PLT Scheme | Racket | ||||||||||||||
| newLISP | |||||||||||||||
| GNU Guile | |||||||||||||||
| Visual LISP | |||||||||||||||
| Clojure | |||||||||||||||
| Arc | |||||||||||||||
| LFE | |||||||||||||||
| Hy | |||||||||||||||
Distinguishing features
[edit]Scheme is primarily a functional programming language. It shares many characteristics with other members of the Lisp programming language family. Scheme's very simple syntax is based on s-expressions, parenthesized lists in which a prefix operator is followed by its arguments. Scheme programs thus consist of sequences of nested lists. Lists are also the main data structure in Scheme, leading to a close equivalence between source code and data formats (homoiconicity). Scheme programs can easily create and evaluate pieces of Scheme code dynamically.
The reliance on lists as data structures is shared by all Lisp dialects. Scheme inherits a rich set of list-processing primitives such as cons, car and cdr from its Lisp progenitors. Scheme uses strictly but dynamically typed variables and supports first class procedures. Thus, procedures can be assigned as values to variables or passed as arguments to procedures.
This section concentrates mainly on innovative features of the language, including those features that distinguish Scheme from other Lisps. Unless stated otherwise, descriptions of features relate to the R5RS standard. In examples provided in this section, the notation "===> result" is used to indicate the result of evaluating the expression on the immediately preceding line. This is the same convention used in R5RS.
Minimalism
[edit]Scheme is a very simple language, much easier to implement than many other languages of comparable expressive power.[17] This ease is attributable to the use of lambda calculus to derive much of the syntax of the language from more primitive forms. For instance of the 23 s-expression-based syntactic constructs defined in the R5RS Scheme standard, 14 are classed as derived or library forms, which can be written as macros involving more fundamental forms, principally lambda. As R5RS (§3.1) says: "The most fundamental of the variable binding constructs is the lambda expression, because all other variable binding constructs can be explained in terms of lambda expressions."[4]
- Fundamental forms: define, lambda, quote, if, define-syntax, let-syntax, letrec-syntax, syntax-rules, set!
- Derived forms: do, let, let*, letrec, cond, case, and, or, begin, named let, delay, unquote, unquote-splicing, quasiquote
Example: a macro to implement let as an expression using lambda to perform the variable bindings.
(define-syntax let
(syntax-rules ()
((let ((var expr) ...) body ...)
((lambda (var ...) body ...) expr ...))))
Thus using let as defined above a Scheme implementation would rewrite "(let ((a 1)(b 2)) (+ b a))" as "((lambda (a b) (+ b a)) 1 2)", which reduces implementation's task to that of coding procedure instantiations.
In 1998, Sussman and Steele remarked that the minimalism of Scheme was not a conscious design goal, but rather the unintended outcome of the design process. "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 realized that the lambda calculus—a small, simple formalism—could serve as the core of a powerful and expressive programming language."[7]
Lexical scope
[edit]Like most modern programming languages and unlike earlier Lisps such as Maclisp, Scheme is lexically scoped: all possible variable bindings in a program unit can be analyzed by reading the text of the program unit without consideration of the contexts in which it may be called. This contrasts with dynamic scoping which was characteristic of early Lisp dialects, because of the processing costs associated with the primitive textual substitution methods used to implement lexical scoping algorithms in compilers and interpreters of the day. In those Lisps, it was perfectly possible for a reference to a free variable inside a procedure to refer to quite distinct bindings external to the procedure, depending on the context of the call.
The impetus to incorporate lexical scoping, which was an unusual scoping model in the early 1970s, into their new version of Lisp, came from Sussman's studies of ALGOL. He suggested that ALGOL-like lexical scoping mechanisms would help to realize their initial goal of implementing Hewitt's Actor model in Lisp.[7]
The key insights on how to introduce lexical scoping into a Lisp dialect were popularized in Sussman and Steele's 1975 Lambda Paper, "Scheme: An Interpreter for Extended Lambda Calculus",[18] where they adopted the concept of the lexical closure (on page 21), which had been described in an AI Memo in 1970 by Joel Moses, who attributed the idea to Peter J. Landin.[19]
Lambda calculus
[edit]Alonzo Church's mathematical notation, the lambda calculus, has inspired Lisp's use of "lambda" as a keyword for introducing a procedure, as well as influencing the development of functional programming techniques involving the use of higher-order functions in Lisp. But early Lisps were not suitable expressions of the lambda calculus because of their treatment of free variables.[7]
A formal lambda system has axioms and a complete calculation rule. It is helpful for the analysis using mathematical logic and tools. In this system, calculation can be seen as a directional deduction. The syntax of lambda calculus follows the recursive expressions from x, y, z, ...,parentheses, spaces, the period and the symbol λ.[20] The function of lambda calculation includes: First, serve as a starting point of powerful mathematical logic. Second, it can reduce the requirement of programmers to consider the implementation details, because it can be used to imitate machine evaluation. Finally, the lambda calculation created a substantial meta-theory.[21]
The introduction of lexical scope resolved the problem by making an equivalence between some forms of lambda notation and their practical expression in a working programming language. Sussman and Steele showed that the new language could be used to elegantly derive all the imperative and declarative semantics of other programming languages including ALGOL and Fortran, and the dynamic scope of other Lisps, by using lambda expressions not as simple procedure instantiations but as "control structures and environment modifiers".[22] They introduced continuation-passing style along with their first description of Scheme in the first of the Lambda Papers, and in subsequent papers, they proceeded to demonstrate the raw power of this practical use of lambda calculus.
Block structure
[edit]Scheme inherits its block structure from earlier block structured languages, particularly ALGOL. In Scheme, blocks are implemented by three binding constructs: let, let* and letrec. For instance, the following construct creates a block in which a symbol called var is bound to the number 10:
(define var "goose")
;; Any reference to var here will be bound to "goose"
(let ((var 10))
;; statements go here. Any reference to var here will be bound to 10.
)
;; Any reference to var here will be bound to "goose"
Blocks can be nested to create arbitrarily complex block structures according to the need of the programmer. The use of block structuring to create local bindings alleviates the risk of namespace collision that can otherwise occur.
One variant of let, let*, permits bindings to refer to variables defined earlier in the same construct, thus:
(let* ((var1 10)
(var2 (+ var1 12)))
;; But the definition of var1 could not refer to var2
)
The other variant, letrec, is designed to enable mutually recursive procedures to be bound to one another.
;; Calculation of Hofstadter's male and female sequences as a list of pairs
(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))
(See Hofstadter's male and female sequences for the definitions used in this example.)
All procedures bound in a single letrec may refer to one another by name, as well as to values of variables defined earlier in the same letrec, but they may not refer to values defined later in the same letrec.
A variant of let, the "named let" form, has an identifier after the let keyword. This binds the let variables to the argument of a procedure whose name is the given identifier and whose body is the body of the let form. The body may be repeated as desired by calling the procedure. The named let is widely used to implement iteration.
Example: a simple counter
(let loop ((n 1))
(if (> n 10)
'()
(cons n
(loop (+ n 1)))))
===> (1 2 3 4 5 6 7 8 9 10)
Like any procedure in Scheme, the procedure created in the named let is a first-class object.
Proper tail recursion
[edit]Scheme has an iteration construct, do, but it is more idiomatic in Scheme to use tail recursion to express iteration. Standard-conforming Scheme implementations are required to optimize tail calls so as to support an unbounded number of active tail calls (R5RS sec. 3.5)[4]—a property the Scheme report describes as proper tail recursion—making it safe for Scheme programmers to write iterative algorithms using recursive structures, which are sometimes more intuitive. Tail recursive procedures and the named let form provide support for iteration using tail recursion.
;; Building a list of squares from 0 to 9:
;; Note: loop is simply an arbitrary symbol used as a label. Any symbol will do.
(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)
First-class continuations
[edit]Continuations in Scheme are first-class objects. Scheme provides the procedure call-with-current-continuation (also known as call/cc) to capture the current continuation by packing it up as an escape procedure bound to a formal argument in a procedure provided by the programmer. (R5RS sec. 6.4)[4] First-class continuations enable the programmer to create non-local control constructs such as iterators, coroutines, and backtracking.
Continuations can be used to emulate the behavior of return statements in imperative programming languages. The following function find-first, given function func and list lst, returns the first element x in lst such that (func x) returns true.
(define (find-first func lst)
(call-with-current-continuation
(lambda (return-immediately)
(for-each (lambda (x)
(if (func x)
(return-immediately 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
The following example, a traditional programmer's puzzle, shows that Scheme can handle continuations as first-class objects, binding them to variables and passing them as arguments to procedures.
(let* ((yin
((lambda (cc) (display "@") cc) (call-with-current-continuation (lambda (c) c))))
(yang
((lambda (cc) (display "*") cc) (call-with-current-continuation (lambda (c) c)))))
(yin yang))
When executed this code displays a counting sequence: @*@**@***@****@*****@******@*******@********...
Shared namespace for procedures and variables
[edit]In contrast to Common Lisp, all data and procedures in Scheme share a common namespace, whereas in Common Lisp functions and data have separate namespaces making it possible for a function and a variable to have the same name, and requiring special notation for referring to a function as a value. This is sometimes known as the "Lisp-1 vs. Lisp-2" distinction, referring to the unified namespace of Scheme and the separate namespaces of Common Lisp.[23]
In Scheme, the same primitives that are used to manipulate and bind data can be used to bind procedures. There is no equivalent of Common Lisp's defun and #' primitives.
;; Variable bound to a number:
(define f 10)
f
===> 10
;; Mutation (altering the bound value)
(set! f (+ f f 6))
f
===> 26
;; Assigning a procedure to the same variable:
(set! f (lambda (n) (+ n 12)))
(f 6)
===> 18
;; Assigning the result of an expression to the same variable:
(set! f (f 1))
f
===> 13
;; functional programming:
(apply + '(1 2 3 4 5 6))
===> 21
(set! f (lambda (n) (+ n 100)))
(map f '(1 2 3))
===> (101 102 103)
Implementation standards
[edit]This subsection documents design decisions that have been taken over the years which have given Scheme a particular character, but are not the direct outcomes of the original design.
Numerical tower
[edit]Scheme specifies a comparatively full set of numerical datatypes including complex and rational types, which is known in Scheme as the numerical tower (R5RS sec. 6.2[4]). The standard treats these as abstractions, and does not commit the implementor to any particular internal representations.
Numbers may have the quality of exactness. An exact number can only be produced by a sequence of exact operations involving other exact numbers—inexactness is thus contagious. The standard specifies that any two implementations must produce equivalent results for all operations resulting in exact numbers.
The R5RS standard specifies procedures exact->inexact and inexact->exact which can be used to change the exactness of a number. inexact->exact produces "the exact number that is numerically closest to the argument". exact->inexact produces "the inexact number that is numerically closest to the argument". The R6RS standard omits these procedures from the main report, but specifies them as R5RS compatibility procedures in the standard library (rnrs r5rs (6)).
In the R5RS standard, Scheme implementations are not required to implement the whole numerical tower, but they must implement "a coherent subset consistent with both the purposes of the implementation and the spirit of the Scheme language" (R5RS sec. 6.2.3).[4] The new R6RS standard does require implementation of the whole tower, and "exact integer objects and exact rational number objects of practically unlimited size and precision, and to implement certain procedures...so they always return exact results when given exact arguments" (R6RS sec. 3.4, sec. 11.7.1).[6]
Example 1: exact arithmetic in an implementation that supports exact rational complex numbers.
;; Sum of three rational real numbers and two rational complex numbers
(define x (+ 1/3 1/4 -1/5 -1/3i 405/50+2/3i))
x
===> 509/60+1/3i
;; Check for exactness.
(exact? x)
===> #t
Example 2: Same arithmetic in an implementation that supports neither exact rational numbers nor complex numbers but does accept real numbers in rational notation.
;; Sum of four rational real numbers
(define xr (+ 1/3 1/4 -1/5 405/50))
;; Sum of two rational real numbers
(define xi (+ -1/3 2/3))
xr
===> 8.48333333333333
xi
===> 0.333333333333333
;; Check for exactness.
(exact? xr)
===> #f
(exact? xi)
===> #f
Both implementations conform to the R5RS standard but the second does not conform to R6RS because it does not implement the full numerical tower.
Delayed evaluation
[edit]Scheme supports delayed evaluation through the delay form and the procedure 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
The lexical context of the original definition of the promise is preserved, and its value is also preserved after the first use of force. The promise is only ever evaluated once.
These primitives, which produce or handle values known as promises, can be used to implement advanced lazy evaluation constructs such as streams.[24]
In the R6RS standard, these are no longer primitives, but instead, are provided as part of the R5RS compatibility library (rnrs r5rs (6)).
In R5RS, a suggested implementation of delay and force is given, implementing the promise as a procedure with no arguments (a thunk) and using memoization to ensure that it is only ever evaluated once, irrespective of the number of times force is called (R5RS sec. 6.4).[4]
SRFI 41 enables the expression of both finite and infinite sequences with extraordinary economy. For example, this is a definition of the Fibonacci sequence using the functions defined in SRFI 41:[24]
;; Define the Fibonacci sequence:
(define fibs
(stream-cons 0
(stream-cons 1
(stream-map +
fibs
(stream-cdr fibs)))))
;; Compute the hundredth number in the sequence:
(stream-ref fibs 99)
===> 218922995834555169026
Order of evaluation of procedure arguments
[edit]Most Lisps specify an order of evaluation for procedure arguments. Scheme does not. Order of evaluation—including the order in which the expression in the operator position is evaluated—may be chosen by an implementation on a call-by-call basis, and the only constraint is that "the effect of any concurrent evaluation of the operator and operand expressions is constrained to be consistent with some sequential order of evaluation." (R5RS sec. 4.1.3)[4]
(let ((ev (lambda(n) (display "Evaluating ")
(display (if (procedure? n) "procedure" n))
(newline) n)))
((ev +) (ev 1) (ev 2)))
===> 3
Evaluating 1
Evaluating 2
Evaluating procedure
ev is a procedure that describes the argument passed to it, then returns the value of the argument. In contrast with other Lisps, the appearance of an expression in the operator position (the first item) of a Scheme expression is quite legal, as long as the result of the expression in the operator position is a procedure.
In calling the procedure "+" to add 1 and 2, the expressions (ev +), (ev 1) and (ev 2) may be evaluated in any order, as long as the effect is not as if they were evaluated in parallel. Thus the following three lines may be displayed in any order by standard Scheme when the above example code is executed, although the text of one line may not be interleaved with another because that would violate the sequential evaluation constraint.
Hygienic macros
[edit]In the R5RS standard and in later reports, the syntax of Scheme can easily be extended via the macro system. The R5RS standard introduced a powerful hygienic macro system that allows the programmer to add new syntactic constructs to the language using a simple pattern matching sublanguage (R5RS sec 4.3).[4] Prior to this, the hygienic macro system had been relegated to an appendix of the R4RS standard, as a "high level" system alongside a "low level" macro system, both of which were treated as extensions to Scheme rather than an essential part of the language.[25]
Implementations of the hygienic macro system, also called syntax-rules, are required to respect the lexical scoping of the rest of the language. This is assured by special naming and scoping rules for macro expansion and avoids common programming errors that can occur in the macro systems of other programming languages. R6RS specifies a more sophisticated transformation system, syntax-case, which has been available as a language extension to R5RS Scheme for some time.
;; Define a macro to implement a variant of "if" with a multi-expression
;; true branch and no false branch.
(define-syntax when
(syntax-rules ()
((when pred exp exps ...)
(if pred (begin exp exps ...)))))
Invocations of macros and procedures bear a close resemblance—both are s-expressions—but they are treated differently. When the compiler encounters an s-expression in the program, it first checks to see if the symbol is defined as a syntactic keyword within the current lexical scope. If so, it then attempts to expand the macro, treating the items in the tail of the s-expression as arguments without compiling code to evaluate them, and this process is repeated recursively until no macro invocations remain. If it is not a syntactic keyword, the compiler compiles code to evaluate the arguments in the tail of the s-expression and then to evaluate the variable represented by the symbol at the head of the s-expression and call it as a procedure with the evaluated tail expressions passed as arguments to it.
Most Scheme implementations also provide additional macro systems. Among popular ones are syntactic closures, explicit renaming macros and define-macro, a non-hygienic macro system similar to defmacro system provided in Common Lisp.
The inability to specify whether or not a macro is hygienic is one of the shortcomings of the macro system. Alternative models for expansion such as scope sets provide a potential solution.[26]
Environments and eval
[edit]Prior to R5RS, Scheme had no standard equivalent of the eval procedure which is ubiquitous in other Lisps, although the first Lambda Paper had described evaluate as "similar to the LISP function EVAL"[18] and the first Revised Report in 1978 replaced this with enclose, which took two arguments. The second, third and fourth revised reports omitted any equivalent of eval.
The reason for this confusion is that in Scheme with its lexical scoping the result of evaluating an expression depends on where it is evaluated. For instance, it is not clear whether the result of evaluating the following expression should be 5 or 6:[27]
(let ((name '+))
(let ((+ *))
(evaluate (list name 2 3))))
If it is evaluated in the outer environment, where name is defined, the result is the sum of the operands. If it is evaluated in the inner environment, where the symbol "+" has been bound to the value of the procedure "*", the result is the product of the two operands.
R5RS resolves this confusion by specifying three procedures that return environments and providing a procedure eval that takes an s-expression and an environment and evaluates the expression in the environment provided. (R5RS sec. 6.5)[4] R6RS extends this by providing a procedure called environment by which the programmer can specify exactly which objects to import into the evaluation environment.
With modern scheme (usually compatible with R5RS) to evaluate this expression, one needs to define a function evaluate which can look like this:
(define (evaluate expr)
(eval expr (interaction-environment)))
interaction-environment is the interpreter's global environment.
Treatment of non-Boolean values in Boolean expressions
[edit]In most dialects of Lisp including Common Lisp, by convention the value NIL evaluates to the value false in a Boolean expression. In Scheme, since the IEEE standard in 1991,[3] all values except #f, including NIL's equivalent in Scheme which is written as '(), evaluate to the value true in a Boolean expression. (R5RS sec. 6.3.1)[4]
Where the constant representing the Boolean value of true is T in most Lisps, in Scheme it is #t.
Disjointness of primitive datatypes
[edit]In Scheme the primitive datatypes are disjoint. Only one of the following predicates can be true of any Scheme object: boolean?, pair?, symbol?, number?, char?, string?, vector?, port?, procedure?. (R5RS sec 3.2)[4]
Within the numerical datatype, by contrast, the numerical values overlap. For example, an integer value satisfies all of the integer?, rational?, real?, complex? and number? predicates at the same time. (R5RS sec 6.2)[4]
Equivalence predicates
[edit]Scheme has three different types of equivalence between arbitrary objects denoted by three different equivalence predicates, relational operators for testing equality, eq?, eqv? and equal?:
eq?evaluates to#funless its parameters represent the same data object in memory;eqv?is generally the same aseq?but treats primitive objects (e.g. characters and numbers) specially so that numbers that represent the same value areeqv?even if they do not refer to the same object;equal?compares data structures such as lists, vectors and strings to determine if they have congruent structure andeqv?contents.(R5RS sec. 6.1)[4]
Type dependent equivalence operations also exist in Scheme: string=? and string-ci=? compare two strings (the latter performs a case-independent comparison); char=? and char-ci=? compare characters; = compares numbers.[4]
Comments
[edit]Up to the R5RS standard, the standard comment in Scheme was a semicolon, which makes the rest of the line invisible to Scheme. Numerous implementations have supported alternative conventions permitting comments to extend for more than a single line, and the R6RS standard permits two of them: an entire s-expression may be turned into a comment (or "commented out") by preceding it with #; (introduced in SRFI 62[28]) and a multiline comment or "block comment" may be produced by surrounding text with #| and |#.
Input/output
[edit]Scheme's input and output is based on the port datatype. (R5RS sec 6.6)[4] R5RS defines two default ports, accessible with the procedures current-input-port and current-output-port, which correspond to the Unix notions of standard input and standard output. Most implementations also provide current-error-port. Redirection of input and standard output is supported in the standard, by standard procedures such as with-input-from-file and with-output-to-file. Most implementations provide string ports with similar redirection capabilities, enabling many normal input-output operations to be performed on string buffers instead of files, using procedures described in SRFI 6.[29] The R6RS standard specifies much more sophisticated and capable port procedures and many new types of port.
The following examples are written in strict R5RS Scheme.
Example 1: With output defaulting to (current-output-port):
(let ((hello0 (lambda() (display "Hello world") (newline))))
(hello0))
Example 2: As 1, but using optional port argument to output procedures
(let ((hello1 (lambda (p) (display "Hello world" p) (newline p))))
(hello1 (current-output-port)))
Example 3: As 1, but output is redirected to a newly created file
;; NB: with-output-to-file is an optional procedure in R5RS
(let ((hello0 (lambda () (display "Hello world") (newline))))
(with-output-to-file "helloworldoutputfile" hello0))
Example 4: As 2, but with explicit file open and port close to send output to file
(let ((hello1 (lambda (p) (display "Hello world" p) (newline p)))
(output-port (open-output-file "helloworldoutputfile")))
(hello1 output-port)
(close-output-port output-port))
Example 5: As 2, but with using call-with-output-file to send output to a file.
(let ((hello1 (lambda (p) (display "Hello world" p) (newline p))))
(call-with-output-file "helloworldoutputfile" hello1))
Similar procedures are provided for input. R5RS Scheme provides the predicates input-port? and output-port?. For character input and output, write-char, read-char, peek-char and char-ready? are provided. For writing and reading Scheme expressions, Scheme provides read and write. On a read operation, the result returned is the end-of-file object if the input port has reached the end of the file, and this can be tested using the predicate eof-object?.
With the standard, SRFI 28 also defines a basic formatting procedure resembling Common Lisp's format function, after which it is named.[30]
Redefinition of standard procedures
[edit]In Scheme, procedures are bound to variables. At R5RS the language standard formally mandated that programs may change the variable bindings of built-in procedures, effectively redefining them. (R5RS "Language changes")[4] For example, + can be extended to accept strings as well as numbers by redefining it:
(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"
In R6RS every binding, including the standard ones, belongs to some library, and all exported bindings are immutable. (R6RS sec 7.1)[6] Because of this, redefinition of standard procedures by mutation is forbidden. Instead, it is possible to import a different procedure under the name of a standard one, which in effect is similar to redefinition.
Nomenclature and naming conventions
[edit]In Standard Scheme, procedures that convert from one datatype to another contain the character string "->" in their name, predicates end with a "?", and procedures that change the value of already-allocated data end with a "!". These conventions are often followed by Scheme programmers.
In formal contexts such as Scheme standards, the word "procedure" is used in preference to "function" to refer to a lambda expression or primitive procedure. In normal usage, the words "procedure" and "function" are used interchangeably. Procedure application is sometimes referred to formally as combination.
As in other Lisps, the term "thunk" is used in Scheme to refer to a procedure with no arguments. The term "proper tail recursion" refers to the property of all Scheme implementations, that they perform tail-call optimization so as to support an indefinite number of active tail calls.
The form of the titles of the standards documents since R3RS, "Revisedn Report on the Algorithmic Language Scheme", is a reference to the title of the ALGOL 60 standard document, "Revised Report on the Algorithmic Language Algol 60," The Summary page of R3RS is closely modeled on the Summary page of the ALGOL 60 Report.[31][32]
Review of standard forms and procedures
[edit]The language is formally defined in the standards R5RS (1998)[4] and R6RS (2007).[6] They describe standard "forms": keywords and accompanying syntax, which provide the control structure of the language, and standard procedures which perform common tasks.
Standard forms
[edit]This table describes the standard forms in Scheme. Some forms appear in more than one row because they cannot easily be classified into a single function in the language.
Forms marked "L" in this table are classed as derived "library" forms in the standard and are often implemented as macros using more fundamental forms in practice, making the task of implementation much easier than in other languages.
| Purpose | Forms |
|---|---|
| Definition | define |
| Binding constructs | lambda, do (L), let (L), let* (L), letrec (L) |
| Conditional evaluation | if, cond (L), case (L), and (L), or (L) |
| Sequential evaluation | begin (*) |
| Iteration | lambda, do (L), named let (L) |
| Syntactic extension | define-syntax, let-syntax, letrec-syntax, syntax-rules (R5RS), syntax-case (R6RS) |
| Quoting | quote('), unquote(,), quasiquote(`), unquote-splicing(,@) |
| Assignment | set! |
| Delayed evaluation | delay (L) |
While begin is defined as a library syntax in R5RS, the expander must know about it to achieve the splicing function. In R6RS it is no longer a library syntax.
Standard procedures
[edit]The following two tables describe the standard procedures in R5RS Scheme. R6RS is far more extensive and a summary of this type would not be practical.
Some procedures appear in more than one row because they cannot easily be classified into a single function in the language.
| Purpose | Procedures |
|---|---|
| Construction | vector, make-vector, make-string, list |
| Equivalence predicates | eq?, eqv?, equal?, string=?, string-ci=?, char=?, char-ci=? |
| Type conversion | vector->list, list->vector, number->string, string->number, symbol->string, string->symbol, char->integer, integer->char, string->list, list->string |
| Numbers | See separate table |
| Strings | string?, make-string, string, string-length, string-ref, string-set!, string=?, string-ci=?, string<? string-ci<?, string<=? string-ci<=?, string>? string-ci>?, string>=? string-ci>=?, substring, string-append, string->list, list->string, string-copy, string-fill! |
| Characters | char?, char=?, char-ci=?, char<? char-ci<?, char<=? char-ci<=?, char>? char-ci>?, char>=? char-ci>=?, char-alphabetic?, char-numeric?, char-whitespace?, char-upper-case?, char-lower-case?, char->integer, integer->char, char-upcase, char-downcase |
| Vectors | make-vector, vector, vector?, vector-length, vector-ref, vector-set!, vector->list, list->vector, vector-fill! |
| Symbols | symbol->string, string->symbol, symbol? |
| Pairs and lists | pair?, cons, car, cdr, set-car!, set-cdr!, null?, list?, list, length, append, reverse, list-tail, list-ref, memq. memv. member, assq, assv, assoc, list->vector, vector->list, list->string, string->list |
| Identity predicates | boolean?, pair?, symbol?, number?, char?, string?, vector?, port?, procedure? |
| Continuations | call-with-current-continuation (call/cc), values, call-with-values, dynamic-wind |
| Environments | eval, scheme-report-environment, null-environment, interaction-environment (optional) |
| Input/output | display, newline, read, write, read-char, write-char, peek-char, char-ready?, eof-object? open-input-file, open-output-file, close-input-port, close-output-port, input-port?, output-port?, current-input-port, current-output-port, call-with-input-file, call-with-output-file, with-input-from-file(optional), with-output-to-file(optional) |
| System interface | load (optional), transcript-on (optional), transcript-off (optional) |
| Delayed evaluation | force |
| Functional programming | procedure?, apply, map, for-each |
| Booleans | boolean? not |
String and character procedures that contain "-ci" in their names perform case-independent comparisons between their arguments: upper case and lower case versions of the same character are taken to be equal.
| Purpose | Procedures |
|---|---|
| Basic arithmetic operators | +, -, *, /, abs, quotient, remainder, modulo, gcd, lcm, expt, sqrt |
| Rational numbers | numerator, denominator, rational?, rationalize |
| Approximation | floor, ceiling, truncate, round |
| Exactness | inexact->exact, exact->inexact, exact?, inexact? |
| Inequalities | <, <= , >, >=, = |
| Miscellaneous predicates | zero?, negative?, positive? odd? even? |
| Maximum and minimum | max, min |
| Trigonometry | sin, cos, tan, asin, acos, atan |
| Exponentials | exp, log |
| Complex numbers | make-rectangular, make-polar, real-part, imag-part, magnitude, angle, complex? |
| Input-output | number->string, string->number |
| Type predicates | integer?, rational?, real?, complex?, number? |
Implementations of - and / that take more than two arguments are defined but left optional at R5RS.
Scheme Requests for Implementation
[edit]Because of Scheme's minimalism, many common procedures and syntactic forms are not defined by the standard. In order to keep the core language small but facilitate standardization of extensions, the Scheme community has a "Scheme Request for Implementation" (SRFI) process by which extension libraries are defined through careful discussion of extension proposals. This promotes code portability. Many of the SRFIs are supported by all or most Scheme implementations.
SRFIs with fairly wide support in different implementations include:[33]
- 0: feature-based conditional expansion construct
- 1: list library
- 4: homogeneous numeric vector datatypes
- 6: basic string ports
- 8: receive, binding to multiple values
- 9: defining record types
- 13: string library
- 14: character-set library
- 16: syntax for procedures of variable arity
- 17: generalized set!
- 18: Multithreading support
- 19: time data types and procedures
- 25: multi-dimensional array primitives
- 26: notation for specializing parameters without currying
- 27: sources of random bits
- 28: basic format strings
- 29: localization
- 30: nested multi-line comments
- 31: a special form for recursive evaluation
- 37: args-fold: a program argument processor
- 39: parameter objects
- 41: streams
- 42: eager comprehensions
- 43: vector library
- 45: primitives for expressing iterative lazy algorithms
- 60: integers as bits
- 61: a more general cond clause
- 66: octet vectors
- 67: compare procedures
Implementations
[edit]The elegant, minimalist design has made Scheme a popular target for language designers, hobbyists, and educators, and because of its small size, that of a typical interpreter, it is also a popular choice for embedded systems and scripting. This has resulted in scores of implementations,[34] most of which differ from each other so much that porting programs from one implementation to another is quite difficult, and the small size of the standard language means that writing a useful program of any great complexity in standard, portable Scheme is almost impossible.[14] The R6RS standard specifies a much broader language, in an attempt to broaden its appeal to programmers.
Almost all implementations provide a traditional Lisp-style read–eval–print loop for development and debugging. Many also compile Scheme programs to executable binary. Support for embedding Scheme code in programs written in other languages is also common, as the relative simplicity of Scheme implementations makes it a popular choice for adding scripting capabilities to larger systems developed in languages such as C. The Gambit, Chicken, and Bigloo Scheme interpreters compile Scheme to C, which makes embedding far easier. Further, Bigloo's compiler can be configured to generate bytecode for the Java virtual machine (JVM), and has an experimental bytecode generator for .NET.
Some implementations support added features. For example, Kawa and JScheme provide integration with Java classes, and the Scheme to C compilers often make it easy to use external libraries written in C, up to allowing the embedding of C code in the Scheme source code. Another example is Pvts, which offers a set of visual tools that support Scheme learning.
Usage
[edit]Scheme is widely used by several[35] schools; in particular, several introductory computer science courses use Scheme in conjunction with the textbook Structure and Interpretation of Computer Programs (SICP).[36] For the past 12 years, PLT has run the ProgramByDesign (formerly TeachScheme!) project, which has exposed close to 600 high school teachers and thousands of high school students to rudimentary Scheme programming. MIT's old introductory programming class 6.001 was taught in Scheme,[37] Although 6.001 has been replaced by more modern courses, SICP continues to be taught at MIT.[38] Likewise, the introductory class at UC Berkeley, CS 61A, was until 2011 taught entirely in Scheme, save minor diversions into Logo to demonstrate dynamic scope. Today, like MIT, Berkeley has replaced the syllabus with a more modern version that is primarily taught in Python 3, but the current syllabus is still based on the old curriculum, and parts of the class are still taught in Scheme.[39]
The textbook How to Design Programs is used by some institutes of higher education for their introductory computer science courses. Worcester Polytechnic Institute uses Scheme exclusively for its introductory course Introduction to Program Design (CS1101).[40] Rose-Hulman Institute of Technology uses Scheme in its more advanced Programming Language Concepts course.[41] Brandeis University's core course, Structure and Interpretations of Computer Programs (COSI121b), is also taught exclusively in Scheme by theoretical computer scientist Harry Mairson.[42] Indiana University's introductory class, C211, is taught entirely in Scheme. A self-paced version of the course, CS 61AS, continues to use Scheme.[43] The introductory computer science courses at Yale and Grinnell College are also taught in Scheme.[44] The former introductory computer science course at the University of Minnesota - Twin Cities, CSCI 1901, also used Scheme as its primary language, followed by a course that introduced students to the Java language;[45] however, following the example of MIT, the department replaced 1901 with the Python-based CSCI 1133,[46] while functional programming is covered in detail in the third-semester course CSCI 2041.[47]
Scheme is/was also used for the following:
- The Document Style Semantics and Specification Language (DSSSL), which provides a method of specifying SGML stylesheets, uses a Scheme subset.[48]
- The well-known open source raster graphics editor GIMP uses TinyScheme as a scripting language.[49]
- Guile has been adopted by GNU project as its official scripting language, and that implementation of Scheme is embedded in such applications as GNU LilyPond and GnuCash as a scripting language for extensions. Likewise, Guile used to be the scripting language for the desktop environment GNOME,[50] and GNOME still has a project that provides Guile bindings to its library stack.[51] There is a project to incorporate Guile into GNU Emacs, GNU's flagship program, replacing the current Emacs Lisp interpreter.[citation needed]
- Elk Scheme is used by Synopsys as a scripting language for its technology CAD (TCAD) tools.[52]
- Shiro Kawai, senior programmer on the movie Final Fantasy: The Spirits Within, used Scheme as a scripting language for managing the real-time rendering engine.[53]
- Google App Inventor for Android uses Scheme, where Kawa is used to compile the Scheme code down to bytecodes for the Java virtual machine running on Android devices.[54]
See also
[edit]- Essentials of Programming Languages, textbook using Scheme as foundation
References
[edit]- ^ "Influences - The Rust Reference". The Rust Reference. Retrieved 2023-04-18.
- ^ Common LISP: The Language, 2nd Ed., Guy L. Steele Jr. Digital Press; 1981. ISBN 978-1-55558-041-4. "Common Lisp is a new dialect of Lisp, a successor to MacLisp, influenced strongly by ZetaLisp and to some extent by Scheme and InterLisp."
- ^ a b 1178-1990 (Reaff 2008) IEEE Standard for the Scheme Programming Language. IEEE part number STDPD14209, unanimously reaffirmed at a meeting of the IEEE-SA Standards Board Standards Review Committee (RevCom), March 26, 2008 (item 6.3 on minutes), reaffirmation minutes accessed October 2009. This document is available from IEEE for purchase only, and not online at time of writing: 2009.
- ^ a b c d e f g h i j k l m n o p q r Richard Kelsey; William Clinger; Jonathan Rees; et al. (August 1998). "Revised5 Report on the Algorithmic Language Scheme". Higher-Order and Symbolic Computation. 11 (1): 7–105. doi:10.1023/A:1010051815785. S2CID 14069423. Retrieved 2012-08-09.
- ^ a b "R7RS final available" (PDF). 2013-07-06.
- ^ a b c d e Sperber, Michael; Dybvig, R. Kent; Flatt, Matthew; Van Straaten, Anton; et al. (August 2007). "Revised6 Report on the Algorithmic Language Scheme (R6RS)". Scheme Steering Committee. Retrieved 2011-09-13.
- ^ a b c d Sussman, Gerald Jay; Steele, Guy L. (1 December 1998). "The First Report on Scheme Revisited". Higher-Order and Symbolic Computation. 11 (4): 399–404. doi:10.1023/A:1010079421970. S2CID 7704398.
- ^ "R6RS Implementations". r6rs.org. Retrieved 2017-11-24.
- ^ Abdulaziz Ghuloum (2007-10-27). "R6RS Libraries and syntax-case system (psyntax)". Ikarus Scheme. Retrieved 2009-10-20.
- ^ Keep, Andrew W.; Dybvig, R. Kent (November 2014). "A run-time representation of scheme record types". Journal of Functional Programming. 24 (6): 675–716. doi:10.1017/S0956796814000203. S2CID 40001845.
- ^ "Revised^6 Report on the Algorithmic Language Scheme, Appendix E: language changes". Scheme Steering Committee. 2007-09-26. Retrieved 2009-10-20.
- ^ "R6RS Electorate". Scheme Steering Committee. 2007. Retrieved 2012-08-09.
- ^ Marc Feeley (compilation) (2007-10-26). "Implementors' intentions concerning R6RS". Scheme Steering Committee, r6rs-discuss mailing list. Archived from the original on 2008-08-20. Retrieved 2012-08-09.
- ^ a b Will Clinger, Marc Feeley, Chris Hanson, Jonathan Rees and Olin Shivers (2009-08-20). "Position Statement (draft)". Scheme Steering Committee. Retrieved 2012-08-09.
{{cite web}}: CS1 maint: multiple names: authors list (link) - ^ "R7RS 9th draft available" (PDF). 2013-04-15.
- ^ Will Clinger (2013-05-10). "extension of voting period". Scheme Language Steering Committee, scheme-reports mailing list. Archived from the original on 2013-07-21. Retrieved 2013-07-07.
- ^ The Scheme 48 implementation is so-named because the interpreter was written by Richard Kelsey and Jonathan Rees in 48 hours (August 6th – 7th, 1986. See Richard Kelsey; Jonathan Rees; Mike Sperber (2008-01-10). "The Incomplete Scheme 48 Reference Manual for release 1.8". Jonathan Rees, s48.org. Retrieved 2012-08-09.
- ^ a b Gerald Jay Sussman & Guy Lewis Steele Jr. (December 1975). "Scheme: An Interpreter for Extended Lambda Calculus" (PDF). AI Memos. AIM-349. MIT AI Lab. hdl:1721.1/5794. Retrieved 23 December 2021.
- ^ Joel Moses (June 1970), The Function of FUNCTION in LISP, or Why the FUNARG Problem Should Be Called the Environment Problem, hdl:1721.1/5854, AI Memo 199,
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.
- ^ van Tonder, André (1 January 2004). "A Lambda Calculus for Quantum Computation". SIAM Journal on Computing. 33 (5): 1109–1135. arXiv:quant-ph/0307150. doi:10.1137/S0097539703432165. S2CID 613571.
- ^ Niehren, J.; Schwinghammer, J.; Smolka, G. (November 2006). "A concurrent lambda calculus with futures" (PDF). Theoretical Computer Science. 364 (3): 338–356. doi:10.1016/j.tcs.2006.08.016.
- ^ Gerald Jay Sussman & Guy Lewis Steele Jr. (March 1976). "Lambda: The Ultimate Imperative". AI Memos. AIM-353. MIT AI Lab. Archived from the original (postscript or PDF) on 2016-05-10. Retrieved 2012-08-09.
- ^ Gabriel, Richard P.; Pitman, Kent (1988). "Technical Issues of Separation in Function Cells and Value Cells". LISP and Symbolic Computation. Vol. 1, no. 1 (published June 1988). pp. 81–101. doi:10.1007/BF01806178. Retrieved 2012-08-09.
- ^ a b Philip L. Bewig (2008-01-24). "SRFI 41: Streams". The SRFI Editors, schemers.org. Retrieved 2012-08-09.
- ^ William Clinger and Jonathan Rees, ed. (1991). "Revised4 Report on the Algorithmic Language Scheme". ACM Lisp Pointers. 4 (3): 1–55. Retrieved 2012-08-09.
- ^ Flatt, Matthew (2016). "Binding as sets of scopes". Proceedings of the 43rd Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages. pp. 705–717. doi:10.1145/2837614.2837620. ISBN 978-1-4503-3549-2. S2CID 15401805.
- ^ Jonathan Rees, The Scheme of Things The June 1992 Meeting Archived 2011-07-16 at the Wayback Machine (postscript), in Lisp Pointers, V(4), October–December 1992. Retrieved 2012-08-09
- ^ Taylor Campbell (2005-07-21). "SRFI 62: S-expression comments". The SRFI Editors, schemers.org. Retrieved 2012-08-09.
- ^ William D Clinger (1999-07-01). "SRFI 6: Basic String Ports". The SRFI Editors, schemers.org. Retrieved 2012-08-09.
- ^ Scott G. Miller (2002-06-25). "SRFI 28: Basic Format Strings". The SRFI Editors, schemers.org. Retrieved 2012-08-09.
- ^ J.W. Backus; F.L. Bauer; J.Green; C. Katz; J. McCarthy P. Naur; et al. (January–April 1960). "Revised Report on the Algorithmic Language Algol 60". Numerische Mathematik, Communications of the ACM, and Journal of the British Computer Society. Retrieved 2012-08-09.
- ^ Jonathan Rees; William Clinger, eds. (December 1986). "Revised(3) Report on the Algorithmic Language Scheme (Dedicated to the Memory of ALGOL 60)". ACM SIGPLAN Notices. 21 (12): 37–79. CiteSeerX 10.1.1.29.3015. doi:10.1145/15042.15043. hdl:1721.1/6424. S2CID 43884422. Retrieved 2012-08-09.
- ^ "Scheme Systems Supporting SRFIs". The SRFI Editors, schemers.org. 2009-08-30. Archived from the original on 2021-06-20. Retrieved 2012-08-09.
- ^ 75 known implementations of Scheme are listed by "scheme-faq-standards". Community Scheme Wiki. 2009-06-25. Retrieved 2009-10-20.
- ^ Ed Martin (2009-07-20). "List of Scheme-using schools". Schemers Inc. Archived from the original on 2009-03-30. Retrieved 2009-10-20.
- ^ "List of SICP-using schools". MIT Press. 1999-01-26. Archived from the original on 2008-06-15. Retrieved 2009-10-20.
- ^ Eric Grimson (Spring 2005). "6.001 Structure and Interpretation of Computer Programs". MIT Open Courseware. Retrieved 2009-10-20.
- ^ Alex Vandiver; Nelson Elhage; et al. (January 2009). "6.184 - Zombies drink caffeinated 6.001". MIT CSAIL. Archived from the original on 2009-08-28. Retrieved 2009-10-20.
- ^ John DeNero (Fall 2019). "Computer Science 61A, Berkeley". Department of Electrical Engineering and Computer Sciences, Berkeley. Archived from the original on 2020-11-11. Retrieved 2019-12-17.
- ^ CS 1101: Introduction to Program Design (A05): course software, Worcester Polytechnic Institute
- ^ "CSSE 304: Programming Language Concepts". Rose-Hulman Institute of Technology.
- ^ "Spring 2021 CS121b Syllabus" (PDF). Brandeis University.
- ^ "Home". berkeley-cs61as.github.io.
- ^ Dana Angluin (Fall 2009). "Introduction to Computer Science (CPSC 201)". The Zoo, Yale University Computer Science Department. Retrieved 2009-10-20.
- ^ Structure of Computer Programming I Archived 2010-06-19 at the Wayback Machine, Computer Science Department, University of Minnesota, Spring 2010 (accessed 2010-01-30).
- ^ CSci Required Class Course Descriptions and Other Information Archived 2019-10-25 at the Wayback Machine, Computer Science Department, University of Minnesota (accessed 2019-10-25)
- ^ CSCI 2041—New Course CSE Curriculum Committee, University of Minnesota (accessed 2019-10-25)
- ^ Robin Cover (2002-02-25). "DSSSL - Document Style Semantics and Specification Language. ISO/IEC 10179:1996". Cover Pages. Retrieved 2012-08-09.
- ^ "The major scripting language for the GIMP that has been attached to it today is Scheme." From Dov Grobgeld (2002). "The GIMP Basic Scheme Tutorial". The GIMP Team. Retrieved 2012-08-09.
- ^ Todd Graham Lewis; David Zoll; Julian Missig (2002). "GNOME FAQ from Internet Archive". The Gnome Team, gnome.org. Archived from the original on 2000-05-22. Retrieved 2012-08-09.
- ^ "guile-gnome". Free Software Foundation. Retrieved 2012-08-09.
- ^ Laurence Brevard (2006-11-09). "Synopsys MAP-inSM Program Update: EDA Interoperability Developers' Forum" (PDF). Synopsis Inc. Retrieved 2012-08-09.
- ^ Kawai, Shiro (October 2002). "Gluing Things Together - Scheme in the Real-time CG Content Production". Proceedings of the First International Lisp Conference, San Francisco: 342–348. Retrieved 2012-08-09.
- ^ Bill Magnuson; Hal Abelson & Mark Friedman (2009-08-11). "Under the Hood of App Inventor for Android". Google Inc, Official Google Research blog. Retrieved 2012-08-09.
Further reading
[edit]- An Introduction to Scheme and its Implementation (a mirror)
- Christopher T. Haynes (1999-06-22). "The Scheme Programming Language Standardization Experience".
- Guy L. Steele Jr., Richard P. Gabriel. "The Evolution of Lisp" (PDF). Archived from the original (PDF) on 2016-06-11.
- Gerald Jay Sussman & Guy Lewis Steele Jr. (December 1975). . Vol. AI Memo 349. MIT Artificial Intelligence Lab. CiteSeerX 10.1.1.128.80 – via Wikisource.
External links
[edit]- scheme.org provides links to many Scheme resources, including the specifications
Scheme Programming at Wikibooks- Introduction to Scheme
Write Yourself a Scheme in 48 Hours at Wikibooks
Media related to Scheme (programming language) at Wikimedia Commons- Scheme Weekly
- Bookmarklet that add Interactive Scheme REPL to any website
Scheme (programming language)
View on GrokipediaHistory
Origins
Scheme was developed in 1975 by Guy L. Steele Jr. and Gerald Jay Sussman at the Massachusetts Institute of Technology's Artificial Intelligence Laboratory as a dialect of Lisp designed to support research in artificial intelligence and programming language semantics.[8][9][10] The language emerged from efforts to create a simpler, more expressive alternative to existing Lisp implementations like MacLisp, which relied on dynamic scoping and lacked certain features for modeling advanced computational models.[11] Steele and Sussman aimed to produce a clean, minimalistic system that could handle complex ideas in AI without the complications of earlier dialects.[12] Key influences on Scheme included Carl Hewitt's Actor model, which inspired the initial exploration of concurrent and distributed computation through autonomous agents, and Peter Landin's ISWIM, a conceptual language that promoted lexical scoping and functional abstractions.[13][11] Unlike MacLisp's dynamic scoping, where variable bindings are resolved at runtime based on the call stack, Scheme adopted lexical scoping to ensure that bindings are determined by the static structure of the source code, providing more predictable and modular behavior.[10] This shift addressed limitations in modeling block-structured programs and supported the language's emphasis on mathematical purity derived from lambda calculus.[12] The initial goals of Scheme focused on establishing clean semantics for applicative-order evaluation (call-by-value), where arguments are fully evaluated before function application; support for block structure through nested lexical scopes; and treating procedures as first-class citizens that could be passed as arguments, returned as values, or stored in data structures.[12] These features enabled straightforward modeling of imperative constructs like recursion and assignment within a functional framework, without relying on complex runtime mechanisms such as stacks.[12] Additionally, the design incorporated proper tail recursion as an optimization goal to facilitate efficient iterative processes, though detailed mechanisms for control flow were refined in subsequent work.[11] Early documentation appeared in the Lambda Papers series, beginning with the 1975 MIT AI Memo 349, "Scheme: An Interpreter for Extended Lambda Calculus," which outlined the language's foundations.[13] This was followed by "Lambda: The Ultimate Imperative" in 1976 (AI Memo 353), demonstrating how Scheme could express traditional programming constructs using lambda applications and continuations.[14] A companion paper, "Lambda: The Ultimate Declarative" (AI Memo 455, 1976), further explored declarative aspects by viewing lambda as a renaming operator.[15] The first implementation was a MacLisp-based interpreter completed in 1975, providing an initial platform for experimentation with these concepts.[16]Evolution of Standards
The evolution of Scheme's standards began with the initial Revised Report on the Algorithmic Language Scheme, published in 1978 by Guy Lewis Steele Jr. and Gerald Jay Sussman, which provided the first informal definition of the language as a statically scoped dialect of Lisp with support for first-class procedures and continuations. This report established Scheme's core syntax and semantics, emphasizing lexical scoping and tail recursion, and served as the foundation for subsequent refinements. During the 1980s, the standards underwent iterative refinements through the Revised Revised Report (R2RS) in 1985, which incorporated contributions from Indiana University researchers and detailed enhancements to the language's implementation, including compiler support.[17] The Revised^3 Report (R3RS), released in 1986 and edited by William Clinger and Jonathan Rees, formalized the report numbering convention (Revised^n Report, or R^nRS) and marked the beginning of broader community involvement, laying groundwork for future standardization efforts that would involve organizations like the IEEE; R3RS served as the primary basis for the IEEE Standard for the Scheme Programming Language (IEEE 1178-1990), published in 1990 to promote portability across implementations.[18] Building on this, the Revised^4 Report (R4RS) in 1991, edited by Clinger and Rees, introduced optional support for hygienic macros to address hygiene issues in macro expansion while maintaining compatibility with prior versions.[19] The Revised^5 Report (R5RS), published in 1998 and edited by Richard Kelsey, Jonathan Rees, and Clinger, became the most widely adopted standard, defining Scheme's core syntax, semantics, and a minimal set of standard libraries; it is a superset of the IEEE standard and notably added explicit distinctions between exact and inexact numbers to ensure numerical precision in computations, with procedures likeexact? and inexact->exact for handling rational and floating-point representations.[4]
In 2007, the Revised^6 Report (R6RS), edited by Michael Sperber and others, shifted toward a modular design with a comprehensive standard library, including support for Unicode, exception handling, and separate library specifications to enhance portability and extensibility.[20] However, it faced criticism for increasing complexity compared to R5RS, with features like mandatory libraries and advanced I/O seen as burdensome for minimal implementations, leading to limited adoption despite implementations like Chez Scheme and Ikarus.[21]
Responding to these concerns, the Revised^7 Report (R7RS) in 2013, coordinated by Alex Shinn and the Scheme Steering Committee, split into a "small" core language for embedding and portability—focusing on simplicity akin to R5RS—and a "large" specification for optional libraries, prioritizing backward compatibility and ease of implementation over R6RS's expansions.[22] The small language, ratified first, emphasized essential features like basic I/O and conditionals while avoiding prescriptive details on advanced topics.[22]
As of 2025, no Revised^8 Report (R8RS) has been ratified, though community workshops and the Scheme Steering Committee continue discussions on potential extensions, such as further library standardization, without a formal timeline for completion.[21]
Recent Developments
Since the release of the R7RS standard in 2013, the Scheme community has sustained its momentum through annual events like the Scheme and Functional Programming Workshop, which provides a platform for presenting novel research in functional programming and language design. The 2025 edition of the workshop was co-located with the ICFP and SPLASH conferences in Singapore on October 16, attracting submissions on high-quality papers and talks exploring innovative applications and extensions of Scheme.[23] Post-R7RS, community efforts have centered on informal discussions and proposals for potential future standards, with particular emphasis on improving concurrency mechanisms and module systems to better support modern software development. These conversations, often advanced through the SRFI process, reflect ongoing attempts to evolve Scheme's core capabilities without a formalized successor report as of 2025; for example, initiatives like R7RS-large encountered setbacks, including the 2023 resignation of its chair amid debates on scope and compatibility.[24][25] Scheme's foundational concepts, such as lexical scoping and closures, continue to exert influence on contemporary languages, evident in JavaScript's widespread use of closures for encapsulation and event handling in web applications. In Rust, recent embeddings of Scheme interpreters highlight its utility for domain-specific scripting, as seen in the 2025 release of scheme-rs, a Rust-based implementation enabling seamless integration of Scheme code within asynchronous Rust programs. Open-source contributions to Scheme have grown steadily, with GitHub hosting an expanding array of repositories for tools, libraries, and implementations that support education, research, and practical use cases by 2025. Curated collections like Awesome Scheme catalog this activity, showcasing resources from parser generators to GPU computing extensions.[26] No new core standard has emerged by 2025, leaving R7RS as the latest official specification, yet the community has directed increased attention toward interoperability with emerging technologies, including WebAssembly for browser-based execution and asynchronous integrations for concurrent systems. Projects such as those presented at the Scheme '24 workshop demonstrate Scheme's deployment on WebAssembly platforms, while scheme-rs facilitates async Rust embeddings for high-performance scripting.[3][27][28]Design Philosophy
Minimalism
Scheme's design emphasizes a compact core language, defined in the Revised^5 Report on Scheme (R5RS) by just six primitive syntactic forms—variable references, literal expressions viaquote, procedure calls, lambda for anonymous functions, if for conditionals, and set! for assignments—along with nine derived forms such as cond, let, and do that expand into primitives.[4] This totals around 15 syntactic forms, a stark contrast to larger Lisp dialects like Common Lisp, whose ANSI standard spans 1153 pages and includes hundreds of built-in forms and extensive libraries.[29] The R5RS report itself is concise at under 60 pages, enabling straightforward specification and fostering implementations that are relatively simple to develop and verify.[4]
The underlying philosophy, articulated in the R5RS, posits that "programming languages should be designed not by piling feature on top of feature, but by removing the weaknesses and restrictions that make additional features appear necessary."[4] Scheme adheres to this by providing a minimal set of primitives from which "everything should be expressible," promoting user-defined abstractions through macros and higher-order functions rather than predefined constructs. This approach yields benefits including easier language implementation due to the reduced specification size, simplified teaching by focusing on foundational concepts, and enhanced program reasoning through a uniform, uncluttered semantics that avoids the complexity of bloated standard libraries.[30][31]
Illustrative of this minimalism, Scheme's core excludes dedicated syntactic forms for input/output operations, delegating them instead to library procedures like display and read, which allows the language to remain pure while extensions handle practical needs.[4] All expressions share a uniform list-based syntax in prefix notation, such as (+ 1 (* 2 3)) for arithmetic, treating code and data homogeneously without special cases for operators or control structures. In comparison to traditional Lisps, Scheme strips away many imperative features—favoring lexical scoping and functional composition over dynamic scoping or extensive mutable state primitives—to achieve greater purity and extensibility.[30][31]
Functional Paradigm
Scheme's functional paradigm is rooted in its foundation as an extension of lambda calculus, where expressions are either applications of functions to arguments or abstractions that define functions, enabling a pure functional subset of the language without side effects. This design allows Scheme to model computations primarily through function composition and application, drawing directly from the substitution semantics of lambda calculus as formalized in its original specification.[32] The language treats procedures as first-class citizens, meaning they can be created dynamically at runtime, stored in data structures, passed as arguments to other procedures, and returned as results from procedures, facilitating higher-order functions that abstract over common patterns.[1] For instance, function composition can be defined as(define compose (lambda (f g) (lambda (x) (f (g x))))), where compose returns a new procedure that applies g followed by f.[1]
Recursion serves as the primary mechanism for iteration in Scheme, with no built-in loop constructs; instead, programs rely on recursive procedure calls, often structured to be tail-recursive for efficiency. The language mandates proper tail-call optimization in implementations, ensuring that tail-recursive calls do not consume additional stack space, thus supporting efficient iterative processes expressed in a functional style.[1] This emphasis on recursion aligns with lambda calculus principles, where fixed-point combinators or block constructs like letrec enable recursive definitions without special primitives, as seen in early examples such as the factorial function (define fact (lambda (n) (if (= n 0) 1 (* n (fact (- n 1)))))).[32]
Scheme encourages immutability through its core data structures, such as lists built with the functional primitives cons, car, and cdr, which create and access persistent structures without inherent mutation. While the language permits side effects via mutable operations like set-car!, it favors referentially transparent code where expressions evaluate to the same value in the same context, promoting pure functional subsets that avoid assignment and focus on immutable data flows.[1] Vectors and other aggregates similarly support functional patterns, with higher-order procedures like map applying functions over collections immutably by default.[1] Although Scheme accommodates imperative styles, its design philosophy prioritizes functional purity, allowing side effects only when explicitly needed while enabling code that adheres to mathematical function semantics.[1]
Language Features
Scoping and Binding
Scheme employs lexical scoping, also known as static scoping, where the binding of a variable is determined by its position in the source code rather than by the runtime call stack. In this model, each use of a variable refers to the binding established by the innermost enclosing region that contains the use, ensuring that variable resolution is predictable and independent of execution order. For instance, in alet expression, variables are bound locally to the expressions within its body, shielding them from outer scopes.[4]
This scoping rule supports Scheme's block structure, which allows for nested environments through constructs like let, let*, and do. The let form creates a new lexical environment by evaluating initialization expressions in the current environment and binding the results to fresh variable locations, with the body then evaluated in this extended environment; bindings are simultaneous, so initializers do not see each other. In contrast, let* binds variables sequentially from left to right, making each binding visible to subsequent initializers and the body, which facilitates dependent definitions. The do construct extends this for iteration, binding loop variables across the entire expression (except in initializers) to enable stepping and testing within a controlled scope, thus preventing global namespace pollution and promoting modular code. These forms collectively provide Algol-like block structure in a Lisp dialect.[33][34]
Procedures and variables share a unified namespace in Scheme, meaning the same identifier can bind either a procedure or a data value within the same lexical environment. For example, the form (define f (lambda (x) (+ x 1))) binds the identifier f to a procedure value in the current environment, allowing f to be invoked like (f 5). This single namespace simplifies the language by treating procedures as first-class data without separate compartments, a design choice that distinguishes Scheme from dialects requiring distinct procedure and variable spaces.[4]
By adopting lexical scoping, Scheme avoids the dynamic scoping used in many early Lisp dialects, such as Maclisp, where bindings are resolved based on the current call stack at runtime, potentially leading to unexpected variable captures and bugs from indirect invocations. Static scoping eliminates these issues by fixing bindings at compile time, enhancing reliability and referential transparency.[4][35]
In lambda expressions, the distinction between free and bound variables underscores lexical scoping: formal parameters are bound within the lambda's body, while free variables—those not bound locally—are resolved in the lexical environment where the lambda is defined and "closed over" by the resulting procedure. For example:
(define x 10)
(define make-adder (lambda (y) ([lambda](/page/Lambda) (z) (+ x y z))))
(define x 10)
(define make-adder (lambda (y) ([lambda](/page/Lambda) (z) (+ x y z))))
x is free in the inner lambda and captures its value from the outer environment, forming a closure that retains access to x even after the outer scope ends; invoking ((make-adder 5) 3) yields 18. This mechanism enables higher-order functions with persistent environment access.[34]
Control Flow Mechanisms
Scheme's control flow mechanisms emphasize functional and declarative programming, eschewing imperative constructs like loops and gotos in favor of recursion and conditional expressions. This design promotes composability and avoids side effects, aligning with the language's minimalistic core. All control is achieved through procedure calls, recursion, or built-in conditional forms, enabling efficient execution via guaranteed tail-call optimization.[1] The primary conditional form isif, which evaluates a predicate and selects between a consequent and an optional alternate based on its truth value (non-falsehood). Its syntax is (if <test> <consequent> [<alternate>]), where <test> is evaluated first; if true, <consequent> is returned, otherwise <alternate> if provided, or an unspecified value. For example:
(if (> 3 2) 'yes 'no) ; => yes
(if (> 3 2) 'yes 'no) ; => yes
cond for sequential predicate evaluation or case for matching against discrete datums. The cond form, with syntax (cond <clause1> <clause2> ...) where each <clause> is (<test> <expression> ...) or (<test> => <recipient>), tests predicates in order until one succeeds, then evaluates the associated expressions or applies the recipient procedure to the test result; an optional else clause serves as default. An example is:
(cond ((> 3 2) 'greater)
(else 'less-or-equal)) ; => greater
(cond ((> 3 2) 'greater)
(else 'less-or-equal)) ; => greater
case matches an evaluated key against datum lists using eqv?, with syntax (case <key> <clause1> <clause2> ...) and clauses like ((<datum> ...) <expression> ...) or ((<datum> ...) => <recipient>); no match yields unspecified results unless else is present. For instance:
(case (* 2 3)
((2 3 5 7) 'prime)
(else 'composite)) ; => unspecified (6 not prime)
(case (* 2 3)
((2 3 5 7) 'prime)
(else 'composite)) ; => unspecified (6 not prime)
(define (fact n)
(if (= n 0)
1
(* n (fact (- n 1))))) ; Recursive call not in tail position
(define (fact n)
(if (= n 0)
1
(* n (fact (- n 1))))) ; Recursive call not in tail position
(define (fact-tail n)
(let loop ((k n) (acc 1))
(if (= k 0)
acc
(loop (- k 1) (* k acc))))) ; Tail call to loop
(define (fact-tail n)
(let loop ((k n) (acc 1))
(if (= k 0)
acc
(loop (- k 1) (* k acc))))) ; Tail call to loop
raise procedure signals an exception by invoking the current handler with an object, while with-exception-handler installs a one-argument handler procedure around a thunk's execution, returning the thunk's result if unraised or the handler's if raised. For structured handling, guard wraps a body in a handler, binding raised conditions to a variable and using cond-like clauses to process them, re-raising if unmatched. An example is:
(guard (condition
((error-object? condition) 'error-caught))
(/ 1 0)) ; => error-caught (catches arithmetic error)
(guard (condition
((error-object? condition) 'error-caught))
(/ 1 0)) ; => error-caught (catches arithmetic error)
call/cc, but these are detailed separately.[1]
First-Class Continuations
Scheme's support for first-class continuations distinguishes it as a language that reifies the control stack, allowing programmers to capture and manipulate the current point of execution as a first-class value. This feature, introduced in early Scheme implementations and standardized in the Revised Fifth Report on Scheme (R5RS), enables powerful abstractions beyond traditional control structures. Continuations represent the remainder of the computation from the capture point onward, facilitating non-local control transfers and programmable control flow. The primary mechanism for capturing a continuation is the procedurecall-with-current-continuation, commonly abbreviated as call/cc. According to R5RS, call/cc is a procedure that takes one argument, proc, which must itself be a procedure of one argument. It passes the current continuation to proc as an "escape procedure," which, when invoked with one argument, abandons the current continuation and tails to the captured one, passing the argument as the result. The escape procedure has unlimited extent, meaning it can be stored in data structures and invoked multiple times, potentially from different threads of control. For instance, to exit a loop upon encountering a negative number, one might write:
(call-with-current-continuation
(lambda (exit)
(for-each (lambda (x)
(if (< x 0)
(exit x)
(display x)))
'(1 2 3 -1 5 6))
'end-of-loop))
; => -1
(call-with-current-continuation
(lambda (exit)
(for-each (lambda (x)
(if (< x 0)
(exit x)
(display x)))
'(1 2 3 -1 5 6))
'end-of-loop))
; => -1
for-each call and invokes it non-locally upon finding a negative value, returning -1 as the overall result. Semantically, the continuation acts as a function that, when applied, restores the program's state to the capture point, effectively implementing a "jump" to that location with the provided value.
First-class continuations enable a range of advanced programming techniques. They support the implementation of coroutines, where multiple routines yield control to one another by capturing and invoking continuations, as demonstrated in early work showing how continuations alone suffice for various coroutine mechanisms without additional primitives. Backtracking search, such as in logic programming or constraint satisfaction, can be achieved by capturing failure continuations and retrying alternatives upon backtrack. Exception handling benefits from continuations by allowing structured unwinding of the stack, similar to raising exceptions but with more flexibility for resumption. Additionally, continuations simulate threads or multitasking in single-threaded environments, as seen in implementations using them for preemptive scheduling and interrupt handling in Scheme systems like MacScheme.
To manage resources during non-local jumps, Scheme provides dynamic-wind, which delimits the dynamic extent of a computation. Per R5RS, dynamic-wind takes three arguments: before, thunk, and after, all procedures of no arguments. It calls before, then thunk (returning its result), then after. The before thunk executes upon entering the dynamic extent of thunk, and after upon exiting, including cases of escape via continuations or re-entry via invocation of a captured continuation. This ensures proper cleanup or setup, such as opening and closing files, even across non-local transfers. For nested extents, inner thunks unwind before outer ones on exit, and wind in reverse on re-entry. An illustrative example simulates a connection protocol:
(let ((path '()) (c #f))
(let ((add (lambda (s) (set! path (cons s path)))))
(dynamic-wind
(lambda () (add 'connect))
(lambda ()
(add (call-with-current-continuation
(lambda (c0) (set! c c0) 'talk1))))
(lambda () (add 'disconnect)))
(if (< (length path) 4)
([c](/page/Continuation) 'talk2)
(reverse path))))
; => (connect talk1 disconnect connect talk2 disconnect)
(let ((path '()) (c #f))
(let ((add (lambda (s) (set! path (cons s path)))))
(dynamic-wind
(lambda () (add 'connect))
(lambda ()
(add (call-with-current-continuation
(lambda (c0) (set! c c0) 'talk1))))
(lambda () (add 'disconnect)))
(if (< (length path) 4)
([c](/page/Continuation) 'talk2)
(reverse path))))
; => (connect talk1 disconnect connect talk2 disconnect)
c triggers the appropriate after and before calls to maintain the connection state. This feature complements Scheme's tail-recursive nature, as continuations align naturally with continuation-passing style for efficient, stack-safe implementations.
Macros and Metaprogramming
Scheme's macro system enables metaprogramming by allowing programmers to define new syntactic forms that expand into existing code at compile time, facilitating the creation of domain-specific languages and abstractions while preserving the language's lexical scoping rules.[34][1] This expansion occurs in stages during compilation, where macro definitions are first transcribed and then repeatedly expanded until only primitive forms remain, ensuring that generated code integrates seamlessly without altering the program's semantics.[34] The primary mechanism for defining macros in modern Scheme issyntax-rules, introduced in the R5RS standard and retained in R7RS, which provides a hygienic pattern-matching system to transform input patterns into output templates.[34][1] For instance, the standard and form is implemented as a macro using syntax-rules to evaluate a sequence of tests left-to-right, short-circuiting on the first false value or returning the last true value:
(define-syntax and
(syntax-rules ()
((and) #t)
((and <test>) <test>)
((and <test1> <test2> ...)
(let ((temp <test1>))
(if temp
(and <test2> ...)
temp)))))
(define-syntax and
(syntax-rules ()
((and) #t)
((and <test>) <test>)
((and <test1> <test2> ...)
(let ((temp <test1>))
(if temp
(and <test2> ...)
temp)))))
(and (= 2 2) (> 2 1)) to #t without risking unintended interactions with surrounding code.[34]
Earlier Scheme reports, such as R4RS, supported defmacro as a simpler but non-hygienic alternative, where the macro body directly manipulated lists as if they were code, often leading to variable capture issues that violated lexical scope.[36] Due to these hygiene problems, defmacro has been deprecated in favor of syntax-rules since R5RS.[34]
Hygienic macros in Scheme prevent accidental variable capture by implicitly renaming bound identifiers during expansion, ensuring that macro-generated bindings do not interfere with or shadow user-defined identifiers from the surrounding lexical context.[37] This mechanism relies on time-stamping identifiers with unique markers during the expansion process—user identifiers receive an initial stamp, while generated ones get fresh stamps—followed by alpha-renaming to avoid conflicts, as formalized in the modified hygiene condition for macro expansion.[37] For example, expanding (or nil v) to (let v:1 nil (if v:1 v:1 v:1)) uses stamping to distinguish the macro's local v from any external one, maintaining referential transparency.[37]
Practical examples illustrate how syntax-rules enables concise metaprogramming; the unless macro, for instance, inverts an if condition to execute a body only if the test is false:
(define-syntax unless
(syntax-rules ()
((unless test stmt1 ... stmtn)
(if (not test)
(begin stmt1 ... stmtn)))))
(define-syntax unless
(syntax-rules ()
((unless test stmt1 ... stmtn)
(if (not test)
(begin stmt1 ... stmtn)))))
when executes its body if the test is true:
(define-syntax when
(syntax-rules ()
((when test stmt1 ... stmtn)
(if test
(begin stmt1 ... stmtn)))))
(define-syntax when
(syntax-rules ()
((when test stmt1 ... stmtn)
(if test
(begin stmt1 ... stmtn)))))
(unless (= 1 2) (display "true branch")) to print nothing and (when (= 1 1) (display "true branch")) to print, demonstrating hygienic expansion without scope pollution.[1] This hygiene, grounded in Scheme's lexical scoping, allows safe extension of the language core.[34]
Standards and Specifications
Core Revised Reports
The core revised reports on the Scheme programming language provide the foundational specifications that define its syntax, semantics, and standard libraries, ensuring a degree of portability across implementations. These reports—R5RS (1998), R6RS (2007), and R7RS (2013 for the small version)—evolved to balance minimalism with practical utility, with each specifying requirements for a minimal conforming implementation to support portable code. A conforming implementation must support all mandatory features, such as the core expressions, standard procedures, and evaluation rules, while optional features like the full numerical tower may vary.[38][20][1] The Revised^5 Report on the Algorithmic Language Scheme (R5RS) organizes the language into key sections covering expressions, program structure, numerical operations, input/output (I/O), and formal syntax. Section 4 details expressions, including primitive types like literals and procedure calls, as well as derived forms such as conditionals and assignments. Section 5 addresses program structure, defining how programs consist of definitions and expressions, with support for syntax definitions via macros. Numerical operations appear in Section 6.3, specifying the numerical tower with exact and inexact representations. Section 6.6 covers I/O through ports, input procedures likeread, and output procedures like display. Section 7 provides a formal BNF syntax and informal semantics to aid precise understanding and implementation. This structure emphasizes a compact, self-contained standard suitable for educational and research use.[38]
In contrast, the Revised^6 Report (R6RS) adopts a modular component model to promote scalability and separate compilation, dividing the specification into a core language report and a separate libraries report. The base library, (rnrs base (6)), exports essential procedures and syntax for arithmetic, data structures, and control flow, serving as the minimal foundation for all programs. Additional libraries, such as those for I/O ((rnrs io ports (6))) and Unicode support, are imported explicitly, enabling modular composition without bloating the core. The rationale for this modularity stems from the need to support larger-scale software development, allowing libraries to be developed, compiled, and linked independently while ensuring interoperability across implementations; this addressed limitations in prior reports by facilitating extensions without altering the base language.[20][39]
The Revised^7 Report (R7RS) splits into small and large versions to accommodate diverse needs, with R7RS-small prioritizing a minimal core akin to R5RS while incorporating lessons from R6RS. R7RS-small defines a core language augmented by eight standard libraries, including (scheme base) for fundamental procedures like define and lambda, (scheme case-lambda) for variadic functions, (scheme char) for character operations, (scheme complex) for complex numbers, (scheme inexact) for inexact arithmetic, (scheme load) for file inclusion, (scheme r5rs) for R5RS compatibility, and (scheme write) for output formatting. This design reverts the perceived complexity of R6RS—such as its extensive mandatory libraries and Unicode requirements—by making most features optional and focusing on a small, embeddable core that supports tail recursion and lexical scoping without mandating advanced modules or exceptions. R7RS-large, an ongoing effort as of November 2025, provides optional extensions through community-defined libraries, allowing implementations to adopt modular enhancements beyond the small core for production use. These portability goals ensure that minimal implementations can run basic Scheme code reliably, while larger ones build upon the standard for broader applications.[1][5]
Numerical and Evaluation Rules
Scheme's numerical system is organized into a tower of subtypes, where every integer is a rational, every rational is a real, and every real is a complex number. This hierarchy is defined by the predicatesnumber?, complex?, real?, rational?, and integer?. The tower supports both exact numbers, which represent mathematical values without approximation (such as integers like 42 or rationals like 1/3), and inexact numbers, which are floating-point approximations (such as 3.14 or 0.5). Implementations may optionally include IEEE-standard infinities (e.g., +inf.0, -inf.0), not-a-number (NaN, e.g., +nan.0), and negative zero (-0.0).[1]
Arithmetic operations in Scheme preserve exactness when possible: if all operands are exact, the result is exact; otherwise, the result is inexact. For example, the expression (+ 1/3 1/3) evaluates to the exact rational 2/3, demonstrating how operations maintain precision without introducing floating-point errors. Mixing exact and inexact values, however, yields an inexact result, as in (+ 1/3 0.1), which produces an approximate value. Predicates like exact? and inexact? distinguish these types, while procedures such as exact and inexact allow conversion between them, though conversion from inexact to exact may raise an error if exact representation is impossible.[1]
Delayed evaluation in Scheme enables lazy computation through the delay and force procedures. The delay form creates a promise object, which represents a delayed expression that is not evaluated until force is applied. Upon forcing, the expression is evaluated in the environment where delay was invoked, and the result is cached in the promise for subsequent forces to avoid recomputation. For instance:
(define x (delay (+ 1 2)))
(force x) ; => 3
(define x (delay (+ 1 2)))
(force x) ; => 3
promise? to check for promise objects and make-promise for explicit creation.[1]
Scheme employs applicative-order evaluation, meaning arguments to a procedure are fully evaluated before the procedure body is executed. The order of evaluating arguments is unspecified but must be consistent with some left-to-right or other sequential order within an implementation; programmers cannot rely on a particular sequence. Consequently, procedures with side effects in arguments provide no guarantees about the order of those effects, emphasizing the importance of avoiding side effects in functional code.[1]
In conditional forms like if, the only false value is #f; all other values, including #t, numbers (even 0), empty lists, and non-empty data structures, are treated as true. This design allows flexible use of any object as a condition without explicit Boolean conversion.[1]
Scheme provides three equivalence predicates for comparing objects: eq?, eqv?, and equal?. The eq? predicate tests for object identity, returning true if and only if its arguments refer to the same location in memory; it is suitable for primitives but may distinguish immutable objects like numbers inconsistently across implementations. For example, (eq? 'a 'a) returns #t, but (eq? 3 3) may or may not, depending on the implementation. The eqv? predicate extends this by considering two objects equivalent if they would produce the same hash value or if they are numbers or characters that compare equal under basic rules, making it reliable for numbers: (eqv? 2 2) always returns #t. Finally, equal? performs structural equivalence, recursively comparing the contents of compound objects like pairs and vectors, ensuring (equal? '(a b) '(a b)) returns #t even if the lists are distinct objects. These predicates form a hierarchy where eq? is the strictest and equal? the most permissive.[1]
SRFIs and Extensions
The Scheme Requests for Implementation (SRFI) process, initiated in 1998 following discussions at the Scheme Workshop in Baltimore, Maryland, provides a community-driven mechanism for proposing and standardizing optional extensions and libraries to the Scheme language.[40] Proposals are submitted to the SRFI editors, undergo public discussion on mailing lists, and, if approved by vote, become final SRFIs with reference implementations encouraged but not required; implementations remain optional across Scheme systems.[41] As of November 2025, 265 SRFIs have been published, covering diverse areas such as data structures, I/O, and syntax extensions.[24] Notable SRFIs include SRFI 1, which defines a comprehensive list-processing library with procedures likefilter and fold for functional list manipulation. SRFI 13 extends string handling with operations such as string-join and string-split, addressing limitations in core Scheme string procedures. SRFI 27 specifies sources of random bits using a linear congruential generator for portable pseudorandom number generation. SRFI 64 establishes a standard API for test suites, enabling portable unit testing with functions like test-begin and test-assert. SRFI 110 introduces "sweet-expressions," an indentation-sensitive syntax extension that enhances readability while preserving s-expression compatibility. These SRFIs exemplify how the process fosters reusable, portable code without altering the core language.
Several SRFIs have been incorporated into proposed standards, particularly the ongoing R7RS-large effort, which includes SRFI 69's basic hash tables as the (scheme [hash-table](/page/Hash_table)) library, providing mutable and immutable hash table operations with customizable equality predicates. Other SRFIs, such as those for bytevectors (SRFI 160) and generators (SRFI 158), are also integrated into R7RS-large, while many remain available as external libraries in implementations like Racket or Guile. This selective adoption allows standards to evolve incrementally.
The SRFI process benefits Scheme by enabling language evolution through community contributions without mandating changes to minimal core reports, promoting interoperability via optional libraries.[40] However, critics note that the proliferation of optional SRFIs can lead to fragmentation, as differing implementation support may hinder portability unless libraries standardize on specific versions.[42]
Syntax and Semantics
Basic Syntax Forms
Scheme employs a minimalist syntax based on S-expressions, which are fully parenthesized lists in prefix notation, allowing uniform representation of both code and data.[1] An S-expression consists of atoms (such as numbers or symbols) or compound forms enclosed in parentheses, where the first element is an operator followed by its operands; for example, the expression(+ 1 2) denotes addition of 1 and 2.[1] This prefix notation eliminates operator precedence issues common in infix languages, as all operations are explicitly grouped by parentheses.[1]
Datum types in Scheme form the building blocks of S-expressions and include literals that evaluate to themselves. The following table summarizes the core datum types:
| Type | Description | Examples |
|---|---|---|
| Booleans | Represent truth values; #f is false, while #t and other non-#f values are true in conditionals. | #t, #f |
| Numbers | Include exact integers, inexact floats, rationals, and complexes; support infinite and NaN values. | 28, 3.14, #i1+2i |
| Characters | Single Unicode or ASCII characters, often used in predicates and I/O. | #\a, #\space |
| Strings | Immutable sequences of characters, delimited by double quotes with escape sequences. | "hello", "\"quote\"" |
| Symbols | Unique identifiers for variables and keywords, case-sensitive and escapable with vertical bars. | lambda, ` |
| Lists | Ordered collections formed by cons cells, proper lists end in empty list (), improper use dots. | (1 2 3), (a . b) |
| Vectors | Fixed-size, mutable arrays accessed by index, prefixed with #(). | #(a b c), #(1 2 3) |
;) initiates a line comment that extends to the end of the line, while block comments use #| to open and |# to close, supporting nesting for embedded comments.[1]
Scheme programs are structured as sequences of definitions and expressions within library bodies or top-level bodies. A library body, declared via (define-library <name>), contains export/import declarations followed by begin forms grouping definitions and expressions; top-level bodies execute definitions and expressions sequentially upon program invocation.[1]
Core special forms provide essential syntactic constructs that do not follow standard procedure application rules, enabling binding, control flow, and definition. The quote form, abbreviated as ', returns its argument unevaluated as a datum; for instance, (quote (a b)) yields the list (a b).[1] The lambda form creates anonymous procedures: (lambda (x) (+ x 1)) defines a function adding 1 to its argument.[1] Conditional execution uses if, which selects between consequent and alternate based on a test: (if (> x 0) 'positive 'non-positive).[1] Assignment occurs via set!, binding a value to an existing variable: (set! count (+ count 1)).[1]
Multi-branch conditionals include cond, which evaluates clauses sequentially until a true test, supporting => for result procedures and else as the final clause:
(cond ((> x 0) 'positive)
((< x 0) 'negative)
(else 'zero))
(cond ((> x 0) 'positive)
((< x 0) 'negative)
(else 'zero))
case form matches an expression against datum lists in clauses: (case (+ 1 1) ((1 2) 'small) ((3) 'medium)).[1] Logical forms and and or short-circuit: (and (= x 1) (> y 0)) evaluates the second only if the first is true, returning the last true value or #f; or returns the first true value or #f if none.[1]
Binding constructs encompass let for parallel bindings:
(let ((x 1) (y 2))
(+ x y))
(let ((x 1) (y 2))
(+ x y))
let* for sequential: (let* ((x 1) (y (+ x 2))) y), where later bindings reference prior ones.[1] The begin form sequences expressions, yielding the last value: (begin (display "hi") 42).[1] Iteration via do initializes variables, tests a condition, and steps in a loop, supporting tail recursion:
(do ((i 0 (+ i 1)))
((> i 5) i)
(print i))
```[](https://standards.scheme.org/official/r7rs.pdf)
Definitions use `define` for variables or procedures: `(define x 5)` or `(define (add a b) (+ a b))`.[](https://standards.scheme.org/official/r7rs.pdf) Syntactic extensions are defined with `define-syntax`, specifying [transformer](/page/Transformer) rules for macros, such as `(define-syntax not (syntax-rules () ((not x) (if x #f #t))))`.[](https://standards.scheme.org/official/r7rs.pdf)
### Built-in Procedures
Scheme's built-in procedures constitute the foundational set of functions available in the initial environment of any conforming [implementation](/page/Implementation), enabling core computations on numbers, lists, strings, vectors, and other data types, as well as basic input/output operations and type predicates. These procedures are specified in the Revised Reports on Scheme, with the core set defined in R5RS and expanded in subsequent standards like R7RS to include additional features while maintaining [backward compatibility](/page/Backward_compatibility) for essential behaviors.[](https://conservatory.scheme.org/schemers/Documents/Standards/R5RS/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf) Implementations are required to provide these procedures with the behaviors described, though redefining them via top-level `define` or `set!` is permitted in most systems; however, such redefinitions have unspecified effects on other built-in procedures and do not alter the standard's assumptions about their fixed semantics.[](https://conservatory.scheme.org/schemers/Documents/Standards/R5RS/r5rs.pdf)
Arithmetic procedures handle numerical operations and comparisons, supporting both exact (e.g., integers, rationals) and inexact (e.g., floating-point) numbers, with results preserving exactness when possible. The addition procedure `+` computes the sum of its arguments (zero or more), returning 0 for no arguments, as in `(+ 1 2 3)` yielding 6.[](https://conservatory.scheme.org/schemers/Documents/Standards/R5RS/r5rs.pdf) Subtraction `-` takes the first argument minus the rest (at least one argument required), or negates a single argument, e.g., `(- 5 3)` returns 2. Multiplication `*` and division `/` similarly operate on zero or more arguments, with identities 1 and an error for division by zero, respectively; for example, `(* 2 3 4)` is 24, and `(/ 10 2)` is 5. Equality `=` checks if all arguments are numerically equal, while order predicates `<`, `>`, `<=`, and `>=` verify monotonicity across two or more arguments, returning `#t` or `#f`. The absolute value `abs` returns the non-negative magnitude of a number, such as `(abs -7)` yielding 7. Predicates `exact?` and `inexact?` (R5RS) test a number's representation type, with R7RS introducing coercers `exact` and `inexact` to convert between them, e.g., `(exact 3.0)` returns 3.[](https://conservatory.scheme.org/schemers/Documents/Standards/R5RS/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)
List operations manipulate Scheme's fundamental linked-list data structure, built from pairs. The constructor `cons` forms a pair from a car (first) and cdr (second) element, e.g., `(cons 1 2)` produces `(1 . 2)`. Accessors `car` and `cdr` retrieve these fields from a pair, with `list` creating proper lists from arguments, as `(list 'a 'b 'c)` yields `'(a b c)`. Concatenation via `append` joins lists (last argument may be non-list), e.g., `(append '(1 2) '(3))` returns `'(1 2 3)`, while `length` counts elements in a proper list, such as `(length '(a b c))` returning 3. Higher-order functions `map` and `for-each` apply a procedure to elements of one or more lists: `map` returns a new list of results, e.g., `(map (lambda (x) (* x x)) '(1 2 3))` gives `'(1 4 9)`, and terminates on the shortest list in R7RS; `for-each` performs side effects without returning values, useful for iteration or filtering-like operations in core Scheme.[](https://conservatory.scheme.org/schemers/Documents/Standards/R5RS/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)
String and vector procedures support mutable sequences for character and general [data storage](/page/Data_storage). `string-append` concatenates strings into a new one, e.g., `(string-append "hello" " " "world")` produces `"hello world"`. Vectors, as fixed-length arrays, are created with `make-vector`, which allocates a vector of given size (optionally filled), such as `(make-vector 3 #f)` yielding `#(#f #f #f)`. Retrieval uses `vector-ref`, e.g., `(vector-ref '#(a b c) 1)` returns `b`, with zero-based indexing and bounds checking required.[](https://conservatory.scheme.org/schemers/Documents/Standards/R5RS/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)
Input/output procedures manage ports as abstract devices for reading and writing data. `open-input-file` opens a named file as an input port, e.g., `(open-input-file "file.scm")`. Reading occurs via `read`, which parses the next Scheme object from a port (defaulting to current input), returning EOF on end-of-file. Writing uses `write` for machine-readable output (with quotes for strings) and `display` for human-readable (without quotes), both to a specified or current output port; for instance, `(display "hello")` prints hello without quotes. The `load` procedure evaluates expressions from a file sequentially, expanding I/O in R7RS to include environment specification and additional file operations like `file-exists?`.[](https://conservatory.scheme.org/schemers/Documents/Standards/R5RS/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)
Type predicates query object kinds, returning booleans to enable dynamic typing checks. Common ones include `pair?` for pairs/lists, `null?` for the empty list, `symbol?` for symbols, `number?` for numbers, `procedure?` for procedures, `boolean?` for `#t`/`#f`, `char?` for characters, `string?` for strings, `vector?` for vectors, and `port?` for ports; e.g., `(pair? '(1 2))` is `#t`, while `(number? 'foo)` is `#f`. R7RS adds specialized predicates like `exact-integer?` and `finite?` for numerical subtypes.[](https://conservatory.scheme.org/schemers/Documents/Standards/R5RS/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)
### Environments and Evaluation
In Scheme, the evaluation process for an expression occurs within a designated environment that supplies bindings for its free variables, determining the values referenced by those variables during execution. The evaluator processes the expression by first handling literals and variables—where variables are looked up by searching successive frames from the current environment outward until a binding is found or an [error](/page/Error) is signaled for unbound variables—and then applying procedures to evaluated operands in an order left unspecified by the standard but required to be consistent across evaluations. This model supports lexical scoping, where bindings are resolved based on the textual structure of the program.[](https://standards.scheme.org/official/r5rs.pdf)[](https://docs.scheme.org/schintro/schintro_121.html)
Environments in Scheme consist of sets of bindings that associate variables with values and syntactic keywords with syntax rules, with the initial global environment providing the outermost layer of bindings for the top-level program. Implementations commonly represent environments as chains of frames, where each frame acts as a local binding context—often structured as an association list of symbol-value pairs or a [hash table](/page/Hash_table) for efficient lookup—and new frames are created dynamically by constructs like `[lambda](/page/Lambda)` or `let` to extend the existing chain without modifying outer bindings. In the Revised⁷ Report on Scheme (R7RS), environments can be specified using procedures such as `scheme-report-environment` and `interaction-environment` for use with `[eval](/page/Eval)`. They are not first-class objects but abstract constructs for binding resolution.[](https://standards.scheme.org/official/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)[](https://docs.scheme.org/schintro/schintro_121.html)
The `eval` procedure provides a mechanism for explicit evaluation of an expression in a given environment, returning the resulting value or signaling an error if evaluation fails. Its syntax is `(eval expression environment-specifier)`, where the environment specifier denotes an existing environment, such as the one returned by `scheme-report-environment` for R5RS compatibility or `interaction-environment` for a mutable REPL context. For instance, the expression `(eval '(* 7 3) (scheme-report-environment 5))` evaluates to `21` in the standard R5RS environment, demonstrating how `eval` isolates evaluation from the current dynamic context. In R7RS, `eval` operates similarly but integrates with library-based environments, such as `(eval '(* 7 3) (scheme-report-environment 7))`, which also yields `21`. Mutable environments permit definitions during evaluation, while immutable ones raise errors on attempts to modify bindings.[](https://standards.scheme.org/official/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)
Scheme's primitive datatypes, including booleans, pairs, symbols, numbers, characters, strings, vectors, and procedures, are defined to be disjoint, ensuring that no single value satisfies more than one of the corresponding type predicates (e.g., `pair?`, `vector?`, `procedure?`). This [orthogonality](/page/Orthogonality) prevents overlap in type identification and supports precise [introspection](/page/Introspection), as each value belongs unequivocally to at most one primitive category, with the remaining types covering all other objects. Environments themselves, while structured as bindings, do not fall under these primitive datatypes but are treated separately as first-class entities in modern standards.[](https://standards.scheme.org/official/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)
The [`apply`](/page/Apply) procedure enables the dynamic application of a procedure to a variadic [list](/page/List) of [arguments](/page/Argument), facilitating flexible procedure invocation where the full argument [list](/page/List) is constructed at runtime. Its [syntax](/page/Hungarian_noun_phrase) is `(apply proc arg₁ … args)`, where `arg₁` through the penultimate arguments are evaluated individually, and `args` must be a proper [list](/page/List) providing the remaining [arguments](/page/Argument), with the entire call required to be a [tail call](/page/Tail_call) in tail-recursive implementations. For example, `(apply + (list 3 4))` evaluates to `7`, effectively passing `3` and `4` as separate arguments to the [addition](/page/Addition) procedure. This contrasts with direct calls like `(+ 3 4)` by allowing the argument tail to be a [data structure](/page/Data_structure), which is essential for higher-order programming patterns.[](https://standards.scheme.org/official/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)
## Implementations
### Major Implementors
Racket is a full-spectrum Scheme implementation that extends the language with modules, contracts, and support for creating domain-specific languages via its `#lang` directive. It includes Typed Racket for [gradual typing](/page/Gradual_typing) and is compatible with R7RS through dedicated packages like `r7rs` and `r7rs-lib`. Widely used for programming language tools and teaching, Racket powers the DrRacket IDE and has been employed in the development of the PLT (Programming Languages [Team](/page/Team)) ecosystem.[](https://racket-lang.org/)[](https://pkgs.racket-lang.org/package/r7rs)
Chez Scheme emphasizes high-performance native code generation through an optimizing compiler that supports whole-program compilation and cross-library optimizations, producing efficient binaries for production environments. It serves as the core [virtual machine](/page/Virtual_machine) for Racket's "CS" variant, enabling faster execution compared to earlier Racket implementations. Chez Scheme is R6RS-compliant and includes features like multi-threading, non-blocking I/O, and a source-level debugger, making it suitable for demanding applications.[](https://cisco.github.io/ChezScheme/)[](https://docs.racket-lang.org/guide/performance.html)
MIT/GNU Scheme is designed primarily for educational purposes, offering an interpreter, compiler, and integrated Emacs-like editor that facilitates interactive development and [debugging](/page/Debugging). It fully supports R5RS and provides a subset of R7RS-small compliance, along with select SRFIs such as SRFI-1 (lists) and SRFI-9 (records). Its lightweight design and tight integration with [Emacs](/page/Emacs) make it a staple in academic settings for teaching Scheme fundamentals.[](https://www.gnu.org/software/mit-scheme/)[](https://www.gnu.org/software/mit-scheme/documentation/stable/mit-scheme-ref/R7RS.html)
Chibi-Scheme is a minimal, embeddable implementation with a small footprint, compiling to a single library without external dependencies, ideal for integration into [C](/page/C) programs as an extension or [scripting language](/page/Scripting_language). It achieves full R7RS-small compliance, supporting the `(scheme base)` library and lightweight VM-based threads for concurrency. Available on multiple platforms including [Linux](/page/Linux), Windows, and [iOS](/page/IOS), it prioritizes portability and ease of embedding.[](https://synthcode.com/scheme/chibi/)[](https://github.com/ashinn/chibi-scheme)
Gambit Scheme provides efficient execution through a [compiler](/page/Compiler) that translates Scheme to [C](/page/C), enabling portable and optimized code generation while maintaining a fast interpreter for development. It supports millions of lightweight threads and Erlang-inspired concurrency primitives, including higher-order channels for messaging. Conforming to R4RS, R5RS, and IEEE standards, Gambit is noted for its reliability in research on concurrent systems.[](http://gambitscheme.org/)[](https://www.gambitscheme.org/latest/manual/)
GNU [Guile](/page/GNU_Guile) is the official Scheme implementation of the GNU Project, designed for embedding in applications as an extension language. It provides full R7RS compliance (with minor exceptions), a [compiler](/page/Compiler) to native code, support for many SRFIs, and features like threads and [foreign function interface](/page/Foreign_function_interface), making it suitable for scripting and application extension.[](https://www.gnu.org/software/guile/)[](https://www.gnu.org/software/guile/manual/html_node/R7RS-Incompatibilities.html)
scheme-rs is a newer, [Rust](/page/Rust)-based Scheme implementation developed in 2025, focusing on seamless embedding within async [Rust](/page/Rust) applications through interoperability features like Rust-Scheme bridges and async task spawning. It targets R6RS compliance with ongoing work toward R7RS-large, incorporating tail-call optimization, concurrent garbage collection, and a [JIT](/page/Jit) [compiler](/page/Compiler) using Cranelift IR. Designed for the async [Rust](/page/Rust) ecosystem, it supports both synchronous and asynchronous contexts via feature flags.[](https://github.com/maplant/scheme-rs)
Implementations vary in their support for SRFIs, with Racket and Chibi-Scheme offering broader coverage through packages and built-ins, while others like MIT/[GNU](/page/GNU) Scheme include select ones for core functionality.[](https://srfi.schemers.org/)
### Optimization Techniques
Scheme implementations employ various optimization techniques to achieve high performance despite the language's dynamic typing and support for first-class continuations. These techniques address challenges such as stack management, code generation, and memory allocation, enabling efficient execution of functional programs. [Tail call](/page/Tail_call) optimization is a cornerstone, mandated by the language standards to ensure space-efficient [recursion](/page/Recursion). Compilation strategies, including native code generation via continuation-passing style (CPS) conversion and just-in-time ([JIT](/page/Jit)) compilation, further enhance speed by producing machine code tailored to the program's structure. Limited [type inference](/page/Type_inference) through flow analysis aids devirtualization, while generational garbage collection handles the frequent allocations typical in functional programming.
Tail call optimization (TCO) is required in proper tail positions to prevent [stack overflow](/page/Stack_overflow) in recursive functions, allowing constant O(1) stack space usage regardless of [recursion](/page/Recursion) depth. According to the Revised^5 Report on Scheme (R5RS), an implementation is properly tail-recursive if it supports an unbounded number of active [tail call](/page/Tail_call)s without additional storage, reusing the caller's continuation for the tail call. This ensures that iterative processes expressed recursively, such as computing factorials, consume no extra space beyond the initial call. For example, the Scheme code `(define (fact n acc) (if (= n 0) acc (fact (- n 1) (* n acc))))` invoked as `(fact 10000 1)` uses constant space due to TCO, as each recursive call is in tail position. The IEEE/ANSI standard for Scheme formalizes this requirement to guarantee space efficiency for portable code, preventing leaks and enabling optimizations like bounded storage for CPS programs.[](https://people.csail.mit.edu/jaffer/r5rs/Proper-tail-recursion.html)[](https://www.cs.tufts.edu/~nr/cs257/archive/will-clinger/proper-tail-space.pdf)
Native code generation in Scheme often relies on CPS conversion, an intermediate representation that explicitly passes continuations as arguments, facilitating direct translation to efficient machine code. This approach simplifies control flow, making continuations nearly cost-free while enabling optimizations like closure flattening and safe-for-space compilation. In CPS, a function like `(lambda (x) (+ x 1))` transforms to a form where evaluation order is explicit, such as `k (let ((v (x))) (let ((v^ (+ v 1))) (k v^)))`, allowing straightforward mapping to assembly or C for native execution. Seminal work on compiling with continuations demonstrates that CPS intermediates support whole-program analysis and register allocation, yielding performance comparable to statically typed languages. Implementations using CPS, such as those compiling to C intermediates, achieve this by serializing expressions and using stack-based allocation with explicit checks.[](https://programm.froscon.org/2013/system/attachments/266/original/scheme-implementation-techniques.pdf)[](https://www.microsoft.com/en-us/research/wp-content/uploads/2007/10/compilingwithcontinuationscontinued.pdf)
Just-in-time ([JIT](/page/Jit)) compilation provides dynamic optimization by generating native code at runtime, adapting to execution patterns without full ahead-of-time [analysis](/page/Analysis). In Racket, the [JIT](/page/Jit) compiler translates [bytecode](/page/Bytecode) to [machine code](/page/Machine_code) incrementally for hot code paths, targeting architectures like x86 and [ARM](/page/Arm). It optimizes tight loops, fixnum arithmetic, and flonum operations, often yielding speedups of several times over interpreted execution with negligible overhead. For instance, arithmetic-heavy kernels benefit from inlined native instructions, making [JIT](/page/Jit) suitable for interactive environments where startup time is traded for runtime efficiency. This approach contrasts with static compilation by incorporating limited runtime information, such as binding contexts from the module system.[](https://docs.racket-lang.org/guide/performance.html)
Due to Scheme's dynamic typing, full [type inference](/page/Type_inference) is limited, but flow analysis enables partial inference for optimizations like devirtualization and [dead code elimination](/page/Dead-code_elimination). Control-flow analysis approximates variable types by tracking possible values across program points, informing decisions such as inlining or specializing calls. Olin Shivers' framework for Scheme uses [abstract interpretation](/page/Abstract_interpretation) to infer types from predicate uses, enabling type-based optimizations in compilers. More advanced flow-sensitive typing integrates with mutable state, inserting runtime checks to refine types and ensure soundness in higher-order settings. For example, analyzing a function's branches can devirtualize procedure calls if flow data shows a single possible type, reducing dispatch overhead without altering semantics. These techniques are polynomial-time, with practical implementations scaling to medium-sized programs.[](https://www.ccs.neu.edu/home/shivers/papers/pldi88.pdf)[](https://people.cs.umass.edu/~arjun/main//papers/guha-esop2011.pdf)
Garbage collection in Scheme implementations commonly uses generational strategies to efficiently manage the high allocation rates from immutable data and closures. Objects are partitioned into generations based on survival age, with younger generations collected more frequently via copying collectors, promoting survivors to older ones. This exploits the generational hypothesis that most objects [die young](/page/Die_Young), reducing pause times and overall work. Andrew Appel's simple two-generation collector, influential in functional language runtimes, demonstrates that minor collections on nursery space suffice for most allocations, with major collections rarer. In practice, parameters like collection trip bytes and [radix](/page/Radix) control [frequency](/page/Frequency), ensuring low latency for interactive use while handling patterns like list construction.[](https://www.cs.princeton.edu/~appel/papers/143.pdf)[](https://www.scheme.com/csug8/smgmt.html)
## Applications
### Educational and Research Use
Scheme has been a cornerstone in [computer science](/page/Computer_science) education, particularly for introducing fundamental programming concepts through its minimalist syntax and support for [functional programming](/page/Functional_programming) paradigms. The textbook *Structure and Interpretation of Computer Programs* (SICP), authored by Harold Abelson, Gerald Jay Sussman, and Julie Sussman, exemplifies this role by using Scheme as the primary language to teach abstraction, modularity, and computational processes in introductory courses such as CS1 and CS2.[](https://web.mit.edu/6.001/6.037/) Published by [MIT Press](/page/MIT_Press), SICP emphasizes building programs that demonstrate general patterns of computation, making Scheme's first-class functions and lexical scoping ideal for illustrating these ideas without the distractions of imperative complexities.[](https://mitp-content-server.mit.edu/books/content/sectbyfn/books_pres_0/6515/sicp.zip/index.html)
At leading universities, Scheme continues to underpin curricula focused on core [computer science](/page/Computer_science) principles. MIT's course 6.037, *Structure and Interpretation of Computer Programs*, employs Scheme to teach thought patterns essential for [computer science](/page/Computer_science), including procedural [abstraction](/page/Abstraction) and data representation, fostering an understanding of how programs model computational processes.[](https://ocw.mit.edu/courses/6-001-structure-and-interpretation-of-computer-programs-spring-2005/) These courses highlight Scheme's ability to emphasize conceptual understanding over syntactic details, aiding students in grasping [abstraction](/page/Abstraction) layers and [evaluation](/page/Evaluation) models.
In academic research, Scheme has profoundly influenced programming language studies, particularly in areas like [lambda calculus](/page/Lambda_calculus), type systems, and program transformation. As an interpreter for extended [lambda calculus](/page/Lambda_calculus), Scheme provides a practical foundation for exploring functional computation and reduction strategies, directly stemming from its original design to support applicative-order evaluation and continuations. It has advanced type systems research through projects like Typed Scheme, which introduces optional typing to dynamically typed Scheme code while preserving [metaprogramming](/page/Metaprogramming) flexibility, enabling safer [program analysis](/page/Program_analysis) without sacrificing expressiveness.[](https://www2.ccs.neu.edu/racket/pubs/popl08-thf.pdf) Scheme's macro system has also facilitated research in program transformation, allowing hygienic expansions that transform source code while maintaining lexical scoping, influencing compiler design and [domain-specific language](/page/Domain-specific_language) creation in [programming language theory](/page/Programming_language_theory).[](https://dl.acm.org/doi/10.1145/62678.62686)
Supporting educational efforts, tools like DrRacket provide an [integrated development environment](/page/Integrated_development_environment) tailored for beginners learning Scheme. DrRacket includes teaching languages derived from Scheme, such as Beginning Student Language, which restrict features to prevent common errors and enforce gradual progression from simple expressions to full functional constructs.[](https://docs.racket-lang.org/drracket/htdp-langs.html) This IDE offers interactive debugging, syntax checking, and visualization tools that make abstract concepts like [recursion](/page/Recursion) and environments more accessible.[](https://docs.racket-lang.org/drracket/)
As of 2025, Scheme maintains relevance in [functional programming](/page/Functional_programming) curricula at universities.
### Production and Embedded Systems
Scheme has found niche applications in production environments through its embeddability and extensibility, particularly in [open-source software](/page/Open-source_software) ecosystems. The Guile implementation serves as the official extension language for [GNU](/page/GNU) projects, enabling Scheme scripting within various tools. For instance, LilyPond, an automated music [engraving](/page/Engraving) system, integrates Guile to allow users to extend its functionality with Scheme code for custom notation and layout definitions.[](https://lilypond.org/doc/v2.24/Documentation/extending/scheme-tutorial) Guile's role extends to other [GNU](/page/GNU) applications, such as [GnuCash](/page/GnuCash) for financial data processing and scripting tasks.
In gaming and tool development, Scheme provides lightweight scripting akin to [Lua](/page/Lua), with implementations like [Gambit](/page/Gambit) enabling portable, high-performance extensions. [Gambit](/page/Gambit) powered the iOS game *Cloud Breaker* (2010), where its cooperative threading system handled game logic and audio processing in a mobile context, demonstrating Scheme's viability for real-time applications.[](https://archive.jlongster.com/Open-Sourcing-My-Gambit-Scheme-iOS-Game-from-2010)
For web development, Racket supports full-stack applications via its built-in [web server](/page/Web_server), which handles HTTP requests and generates dynamic content for server-side scripting.[](https://docs.racket-lang.org/continue/) Chicken Scheme complements this with the Spiffy library, a lightweight [web server](/page/Web_server) that includes CGI handlers for deploying Scheme scripts in traditional web environments.[](http://wiki.call-cc.org/egg/spiffy-cgi-handlers)
Embedded deployments leverage Scheme's compact runtimes for resource-limited systems. Chibi-Scheme, a minimal library with no external dependencies, is optimized for integration into C applications, making it suitable for IoT devices where footprint and embedding ease are critical.[](https://synthcode.com/scheme/chibi/) Gambit Scheme extends to mobile and embedded scenarios, supporting cross-platform development through frameworks like LambdaNative, which targets iOS, Android, and web assembly for production tools.[](http://gambitscheme.org/) In 2025, scheme-rs emerged as a specialized implementation for asynchronous Rust ecosystems, providing seamless interop to embed Scheme in high-concurrency applications like networked services.
Notable production uses include Bigloo Scheme in server-side contexts via the Hop platform, which compiles Scheme to efficient executables for blending server and client logic in web applications.[](https://www.call-with.cc/post/state-of-scheme-to-javascript) These examples illustrate transitions from research prototypes—such as early [Gambit](/page/Gambit) experiments in portable interpreters—to mature deployments in tools like [LilyPond](/page/LilyPond).[](http://www.gambitscheme.org/research/)
Despite these successes, Scheme in production encounters challenges in [performance tuning](/page/Performance_tuning), often necessitating [ahead-of-time compilation](/page/Ahead-of-time_compilation) to C or JVM backends to match native speeds in compute-intensive tasks.[](https://www-sop.inria.fr/indes/fp/Bigloo/) Additionally, its library ecosystem remains smaller and more fragmented than those of Python or [Java](/page/Java), requiring developers to build or adapt extensions for broader integration, though community efforts like SRFIs address standardization gaps.[](https://jointhefreeworld.org/blog/articles/lisps/scheme-and-lisps-are-great-for-production/index.html)
(do ((i 0 (+ i 1)))
((> i 5) i)
(print i))
```[](https://standards.scheme.org/official/r7rs.pdf)
Definitions use `define` for variables or procedures: `(define x 5)` or `(define (add a b) (+ a b))`.[](https://standards.scheme.org/official/r7rs.pdf) Syntactic extensions are defined with `define-syntax`, specifying [transformer](/page/Transformer) rules for macros, such as `(define-syntax not (syntax-rules () ((not x) (if x #f #t))))`.[](https://standards.scheme.org/official/r7rs.pdf)
### Built-in Procedures
Scheme's built-in procedures constitute the foundational set of functions available in the initial environment of any conforming [implementation](/page/Implementation), enabling core computations on numbers, lists, strings, vectors, and other data types, as well as basic input/output operations and type predicates. These procedures are specified in the Revised Reports on Scheme, with the core set defined in R5RS and expanded in subsequent standards like R7RS to include additional features while maintaining [backward compatibility](/page/Backward_compatibility) for essential behaviors.[](https://conservatory.scheme.org/schemers/Documents/Standards/R5RS/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf) Implementations are required to provide these procedures with the behaviors described, though redefining them via top-level `define` or `set!` is permitted in most systems; however, such redefinitions have unspecified effects on other built-in procedures and do not alter the standard's assumptions about their fixed semantics.[](https://conservatory.scheme.org/schemers/Documents/Standards/R5RS/r5rs.pdf)
Arithmetic procedures handle numerical operations and comparisons, supporting both exact (e.g., integers, rationals) and inexact (e.g., floating-point) numbers, with results preserving exactness when possible. The addition procedure `+` computes the sum of its arguments (zero or more), returning 0 for no arguments, as in `(+ 1 2 3)` yielding 6.[](https://conservatory.scheme.org/schemers/Documents/Standards/R5RS/r5rs.pdf) Subtraction `-` takes the first argument minus the rest (at least one argument required), or negates a single argument, e.g., `(- 5 3)` returns 2. Multiplication `*` and division `/` similarly operate on zero or more arguments, with identities 1 and an error for division by zero, respectively; for example, `(* 2 3 4)` is 24, and `(/ 10 2)` is 5. Equality `=` checks if all arguments are numerically equal, while order predicates `<`, `>`, `<=`, and `>=` verify monotonicity across two or more arguments, returning `#t` or `#f`. The absolute value `abs` returns the non-negative magnitude of a number, such as `(abs -7)` yielding 7. Predicates `exact?` and `inexact?` (R5RS) test a number's representation type, with R7RS introducing coercers `exact` and `inexact` to convert between them, e.g., `(exact 3.0)` returns 3.[](https://conservatory.scheme.org/schemers/Documents/Standards/R5RS/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)
List operations manipulate Scheme's fundamental linked-list data structure, built from pairs. The constructor `cons` forms a pair from a car (first) and cdr (second) element, e.g., `(cons 1 2)` produces `(1 . 2)`. Accessors `car` and `cdr` retrieve these fields from a pair, with `list` creating proper lists from arguments, as `(list 'a 'b 'c)` yields `'(a b c)`. Concatenation via `append` joins lists (last argument may be non-list), e.g., `(append '(1 2) '(3))` returns `'(1 2 3)`, while `length` counts elements in a proper list, such as `(length '(a b c))` returning 3. Higher-order functions `map` and `for-each` apply a procedure to elements of one or more lists: `map` returns a new list of results, e.g., `(map (lambda (x) (* x x)) '(1 2 3))` gives `'(1 4 9)`, and terminates on the shortest list in R7RS; `for-each` performs side effects without returning values, useful for iteration or filtering-like operations in core Scheme.[](https://conservatory.scheme.org/schemers/Documents/Standards/R5RS/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)
String and vector procedures support mutable sequences for character and general [data storage](/page/Data_storage). `string-append` concatenates strings into a new one, e.g., `(string-append "hello" " " "world")` produces `"hello world"`. Vectors, as fixed-length arrays, are created with `make-vector`, which allocates a vector of given size (optionally filled), such as `(make-vector 3 #f)` yielding `#(#f #f #f)`. Retrieval uses `vector-ref`, e.g., `(vector-ref '#(a b c) 1)` returns `b`, with zero-based indexing and bounds checking required.[](https://conservatory.scheme.org/schemers/Documents/Standards/R5RS/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)
Input/output procedures manage ports as abstract devices for reading and writing data. `open-input-file` opens a named file as an input port, e.g., `(open-input-file "file.scm")`. Reading occurs via `read`, which parses the next Scheme object from a port (defaulting to current input), returning EOF on end-of-file. Writing uses `write` for machine-readable output (with quotes for strings) and `display` for human-readable (without quotes), both to a specified or current output port; for instance, `(display "hello")` prints hello without quotes. The `load` procedure evaluates expressions from a file sequentially, expanding I/O in R7RS to include environment specification and additional file operations like `file-exists?`.[](https://conservatory.scheme.org/schemers/Documents/Standards/R5RS/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)
Type predicates query object kinds, returning booleans to enable dynamic typing checks. Common ones include `pair?` for pairs/lists, `null?` for the empty list, `symbol?` for symbols, `number?` for numbers, `procedure?` for procedures, `boolean?` for `#t`/`#f`, `char?` for characters, `string?` for strings, `vector?` for vectors, and `port?` for ports; e.g., `(pair? '(1 2))` is `#t`, while `(number? 'foo)` is `#f`. R7RS adds specialized predicates like `exact-integer?` and `finite?` for numerical subtypes.[](https://conservatory.scheme.org/schemers/Documents/Standards/R5RS/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)
### Environments and Evaluation
In Scheme, the evaluation process for an expression occurs within a designated environment that supplies bindings for its free variables, determining the values referenced by those variables during execution. The evaluator processes the expression by first handling literals and variables—where variables are looked up by searching successive frames from the current environment outward until a binding is found or an [error](/page/Error) is signaled for unbound variables—and then applying procedures to evaluated operands in an order left unspecified by the standard but required to be consistent across evaluations. This model supports lexical scoping, where bindings are resolved based on the textual structure of the program.[](https://standards.scheme.org/official/r5rs.pdf)[](https://docs.scheme.org/schintro/schintro_121.html)
Environments in Scheme consist of sets of bindings that associate variables with values and syntactic keywords with syntax rules, with the initial global environment providing the outermost layer of bindings for the top-level program. Implementations commonly represent environments as chains of frames, where each frame acts as a local binding context—often structured as an association list of symbol-value pairs or a [hash table](/page/Hash_table) for efficient lookup—and new frames are created dynamically by constructs like `[lambda](/page/Lambda)` or `let` to extend the existing chain without modifying outer bindings. In the Revised⁷ Report on Scheme (R7RS), environments can be specified using procedures such as `scheme-report-environment` and `interaction-environment` for use with `[eval](/page/Eval)`. They are not first-class objects but abstract constructs for binding resolution.[](https://standards.scheme.org/official/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)[](https://docs.scheme.org/schintro/schintro_121.html)
The `eval` procedure provides a mechanism for explicit evaluation of an expression in a given environment, returning the resulting value or signaling an error if evaluation fails. Its syntax is `(eval expression environment-specifier)`, where the environment specifier denotes an existing environment, such as the one returned by `scheme-report-environment` for R5RS compatibility or `interaction-environment` for a mutable REPL context. For instance, the expression `(eval '(* 7 3) (scheme-report-environment 5))` evaluates to `21` in the standard R5RS environment, demonstrating how `eval` isolates evaluation from the current dynamic context. In R7RS, `eval` operates similarly but integrates with library-based environments, such as `(eval '(* 7 3) (scheme-report-environment 7))`, which also yields `21`. Mutable environments permit definitions during evaluation, while immutable ones raise errors on attempts to modify bindings.[](https://standards.scheme.org/official/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)
Scheme's primitive datatypes, including booleans, pairs, symbols, numbers, characters, strings, vectors, and procedures, are defined to be disjoint, ensuring that no single value satisfies more than one of the corresponding type predicates (e.g., `pair?`, `vector?`, `procedure?`). This [orthogonality](/page/Orthogonality) prevents overlap in type identification and supports precise [introspection](/page/Introspection), as each value belongs unequivocally to at most one primitive category, with the remaining types covering all other objects. Environments themselves, while structured as bindings, do not fall under these primitive datatypes but are treated separately as first-class entities in modern standards.[](https://standards.scheme.org/official/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)
The [`apply`](/page/Apply) procedure enables the dynamic application of a procedure to a variadic [list](/page/List) of [arguments](/page/Argument), facilitating flexible procedure invocation where the full argument [list](/page/List) is constructed at runtime. Its [syntax](/page/Hungarian_noun_phrase) is `(apply proc arg₁ … args)`, where `arg₁` through the penultimate arguments are evaluated individually, and `args` must be a proper [list](/page/List) providing the remaining [arguments](/page/Argument), with the entire call required to be a [tail call](/page/Tail_call) in tail-recursive implementations. For example, `(apply + (list 3 4))` evaluates to `7`, effectively passing `3` and `4` as separate arguments to the [addition](/page/Addition) procedure. This contrasts with direct calls like `(+ 3 4)` by allowing the argument tail to be a [data structure](/page/Data_structure), which is essential for higher-order programming patterns.[](https://standards.scheme.org/official/r5rs.pdf)[](https://standards.scheme.org/official/r7rs.pdf)
## Implementations
### Major Implementors
Racket is a full-spectrum Scheme implementation that extends the language with modules, contracts, and support for creating domain-specific languages via its `#lang` directive. It includes Typed Racket for [gradual typing](/page/Gradual_typing) and is compatible with R7RS through dedicated packages like `r7rs` and `r7rs-lib`. Widely used for programming language tools and teaching, Racket powers the DrRacket IDE and has been employed in the development of the PLT (Programming Languages [Team](/page/Team)) ecosystem.[](https://racket-lang.org/)[](https://pkgs.racket-lang.org/package/r7rs)
Chez Scheme emphasizes high-performance native code generation through an optimizing compiler that supports whole-program compilation and cross-library optimizations, producing efficient binaries for production environments. It serves as the core [virtual machine](/page/Virtual_machine) for Racket's "CS" variant, enabling faster execution compared to earlier Racket implementations. Chez Scheme is R6RS-compliant and includes features like multi-threading, non-blocking I/O, and a source-level debugger, making it suitable for demanding applications.[](https://cisco.github.io/ChezScheme/)[](https://docs.racket-lang.org/guide/performance.html)
MIT/GNU Scheme is designed primarily for educational purposes, offering an interpreter, compiler, and integrated Emacs-like editor that facilitates interactive development and [debugging](/page/Debugging). It fully supports R5RS and provides a subset of R7RS-small compliance, along with select SRFIs such as SRFI-1 (lists) and SRFI-9 (records). Its lightweight design and tight integration with [Emacs](/page/Emacs) make it a staple in academic settings for teaching Scheme fundamentals.[](https://www.gnu.org/software/mit-scheme/)[](https://www.gnu.org/software/mit-scheme/documentation/stable/mit-scheme-ref/R7RS.html)
Chibi-Scheme is a minimal, embeddable implementation with a small footprint, compiling to a single library without external dependencies, ideal for integration into [C](/page/C) programs as an extension or [scripting language](/page/Scripting_language). It achieves full R7RS-small compliance, supporting the `(scheme base)` library and lightweight VM-based threads for concurrency. Available on multiple platforms including [Linux](/page/Linux), Windows, and [iOS](/page/IOS), it prioritizes portability and ease of embedding.[](https://synthcode.com/scheme/chibi/)[](https://github.com/ashinn/chibi-scheme)
Gambit Scheme provides efficient execution through a [compiler](/page/Compiler) that translates Scheme to [C](/page/C), enabling portable and optimized code generation while maintaining a fast interpreter for development. It supports millions of lightweight threads and Erlang-inspired concurrency primitives, including higher-order channels for messaging. Conforming to R4RS, R5RS, and IEEE standards, Gambit is noted for its reliability in research on concurrent systems.[](http://gambitscheme.org/)[](https://www.gambitscheme.org/latest/manual/)
GNU [Guile](/page/GNU_Guile) is the official Scheme implementation of the GNU Project, designed for embedding in applications as an extension language. It provides full R7RS compliance (with minor exceptions), a [compiler](/page/Compiler) to native code, support for many SRFIs, and features like threads and [foreign function interface](/page/Foreign_function_interface), making it suitable for scripting and application extension.[](https://www.gnu.org/software/guile/)[](https://www.gnu.org/software/guile/manual/html_node/R7RS-Incompatibilities.html)
scheme-rs is a newer, [Rust](/page/Rust)-based Scheme implementation developed in 2025, focusing on seamless embedding within async [Rust](/page/Rust) applications through interoperability features like Rust-Scheme bridges and async task spawning. It targets R6RS compliance with ongoing work toward R7RS-large, incorporating tail-call optimization, concurrent garbage collection, and a [JIT](/page/Jit) [compiler](/page/Compiler) using Cranelift IR. Designed for the async [Rust](/page/Rust) ecosystem, it supports both synchronous and asynchronous contexts via feature flags.[](https://github.com/maplant/scheme-rs)
Implementations vary in their support for SRFIs, with Racket and Chibi-Scheme offering broader coverage through packages and built-ins, while others like MIT/[GNU](/page/GNU) Scheme include select ones for core functionality.[](https://srfi.schemers.org/)
### Optimization Techniques
Scheme implementations employ various optimization techniques to achieve high performance despite the language's dynamic typing and support for first-class continuations. These techniques address challenges such as stack management, code generation, and memory allocation, enabling efficient execution of functional programs. [Tail call](/page/Tail_call) optimization is a cornerstone, mandated by the language standards to ensure space-efficient [recursion](/page/Recursion). Compilation strategies, including native code generation via continuation-passing style (CPS) conversion and just-in-time ([JIT](/page/Jit)) compilation, further enhance speed by producing machine code tailored to the program's structure. Limited [type inference](/page/Type_inference) through flow analysis aids devirtualization, while generational garbage collection handles the frequent allocations typical in functional programming.
Tail call optimization (TCO) is required in proper tail positions to prevent [stack overflow](/page/Stack_overflow) in recursive functions, allowing constant O(1) stack space usage regardless of [recursion](/page/Recursion) depth. According to the Revised^5 Report on Scheme (R5RS), an implementation is properly tail-recursive if it supports an unbounded number of active [tail call](/page/Tail_call)s without additional storage, reusing the caller's continuation for the tail call. This ensures that iterative processes expressed recursively, such as computing factorials, consume no extra space beyond the initial call. For example, the Scheme code `(define (fact n acc) (if (= n 0) acc (fact (- n 1) (* n acc))))` invoked as `(fact 10000 1)` uses constant space due to TCO, as each recursive call is in tail position. The IEEE/ANSI standard for Scheme formalizes this requirement to guarantee space efficiency for portable code, preventing leaks and enabling optimizations like bounded storage for CPS programs.[](https://people.csail.mit.edu/jaffer/r5rs/Proper-tail-recursion.html)[](https://www.cs.tufts.edu/~nr/cs257/archive/will-clinger/proper-tail-space.pdf)
Native code generation in Scheme often relies on CPS conversion, an intermediate representation that explicitly passes continuations as arguments, facilitating direct translation to efficient machine code. This approach simplifies control flow, making continuations nearly cost-free while enabling optimizations like closure flattening and safe-for-space compilation. In CPS, a function like `(lambda (x) (+ x 1))` transforms to a form where evaluation order is explicit, such as `k (let ((v (x))) (let ((v^ (+ v 1))) (k v^)))`, allowing straightforward mapping to assembly or C for native execution. Seminal work on compiling with continuations demonstrates that CPS intermediates support whole-program analysis and register allocation, yielding performance comparable to statically typed languages. Implementations using CPS, such as those compiling to C intermediates, achieve this by serializing expressions and using stack-based allocation with explicit checks.[](https://programm.froscon.org/2013/system/attachments/266/original/scheme-implementation-techniques.pdf)[](https://www.microsoft.com/en-us/research/wp-content/uploads/2007/10/compilingwithcontinuationscontinued.pdf)
Just-in-time ([JIT](/page/Jit)) compilation provides dynamic optimization by generating native code at runtime, adapting to execution patterns without full ahead-of-time [analysis](/page/Analysis). In Racket, the [JIT](/page/Jit) compiler translates [bytecode](/page/Bytecode) to [machine code](/page/Machine_code) incrementally for hot code paths, targeting architectures like x86 and [ARM](/page/Arm). It optimizes tight loops, fixnum arithmetic, and flonum operations, often yielding speedups of several times over interpreted execution with negligible overhead. For instance, arithmetic-heavy kernels benefit from inlined native instructions, making [JIT](/page/Jit) suitable for interactive environments where startup time is traded for runtime efficiency. This approach contrasts with static compilation by incorporating limited runtime information, such as binding contexts from the module system.[](https://docs.racket-lang.org/guide/performance.html)
Due to Scheme's dynamic typing, full [type inference](/page/Type_inference) is limited, but flow analysis enables partial inference for optimizations like devirtualization and [dead code elimination](/page/Dead-code_elimination). Control-flow analysis approximates variable types by tracking possible values across program points, informing decisions such as inlining or specializing calls. Olin Shivers' framework for Scheme uses [abstract interpretation](/page/Abstract_interpretation) to infer types from predicate uses, enabling type-based optimizations in compilers. More advanced flow-sensitive typing integrates with mutable state, inserting runtime checks to refine types and ensure soundness in higher-order settings. For example, analyzing a function's branches can devirtualize procedure calls if flow data shows a single possible type, reducing dispatch overhead without altering semantics. These techniques are polynomial-time, with practical implementations scaling to medium-sized programs.[](https://www.ccs.neu.edu/home/shivers/papers/pldi88.pdf)[](https://people.cs.umass.edu/~arjun/main//papers/guha-esop2011.pdf)
Garbage collection in Scheme implementations commonly uses generational strategies to efficiently manage the high allocation rates from immutable data and closures. Objects are partitioned into generations based on survival age, with younger generations collected more frequently via copying collectors, promoting survivors to older ones. This exploits the generational hypothesis that most objects [die young](/page/Die_Young), reducing pause times and overall work. Andrew Appel's simple two-generation collector, influential in functional language runtimes, demonstrates that minor collections on nursery space suffice for most allocations, with major collections rarer. In practice, parameters like collection trip bytes and [radix](/page/Radix) control [frequency](/page/Frequency), ensuring low latency for interactive use while handling patterns like list construction.[](https://www.cs.princeton.edu/~appel/papers/143.pdf)[](https://www.scheme.com/csug8/smgmt.html)
## Applications
### Educational and Research Use
Scheme has been a cornerstone in [computer science](/page/Computer_science) education, particularly for introducing fundamental programming concepts through its minimalist syntax and support for [functional programming](/page/Functional_programming) paradigms. The textbook *Structure and Interpretation of Computer Programs* (SICP), authored by Harold Abelson, Gerald Jay Sussman, and Julie Sussman, exemplifies this role by using Scheme as the primary language to teach abstraction, modularity, and computational processes in introductory courses such as CS1 and CS2.[](https://web.mit.edu/6.001/6.037/) Published by [MIT Press](/page/MIT_Press), SICP emphasizes building programs that demonstrate general patterns of computation, making Scheme's first-class functions and lexical scoping ideal for illustrating these ideas without the distractions of imperative complexities.[](https://mitp-content-server.mit.edu/books/content/sectbyfn/books_pres_0/6515/sicp.zip/index.html)
At leading universities, Scheme continues to underpin curricula focused on core [computer science](/page/Computer_science) principles. MIT's course 6.037, *Structure and Interpretation of Computer Programs*, employs Scheme to teach thought patterns essential for [computer science](/page/Computer_science), including procedural [abstraction](/page/Abstraction) and data representation, fostering an understanding of how programs model computational processes.[](https://ocw.mit.edu/courses/6-001-structure-and-interpretation-of-computer-programs-spring-2005/) These courses highlight Scheme's ability to emphasize conceptual understanding over syntactic details, aiding students in grasping [abstraction](/page/Abstraction) layers and [evaluation](/page/Evaluation) models.
In academic research, Scheme has profoundly influenced programming language studies, particularly in areas like [lambda calculus](/page/Lambda_calculus), type systems, and program transformation. As an interpreter for extended [lambda calculus](/page/Lambda_calculus), Scheme provides a practical foundation for exploring functional computation and reduction strategies, directly stemming from its original design to support applicative-order evaluation and continuations. It has advanced type systems research through projects like Typed Scheme, which introduces optional typing to dynamically typed Scheme code while preserving [metaprogramming](/page/Metaprogramming) flexibility, enabling safer [program analysis](/page/Program_analysis) without sacrificing expressiveness.[](https://www2.ccs.neu.edu/racket/pubs/popl08-thf.pdf) Scheme's macro system has also facilitated research in program transformation, allowing hygienic expansions that transform source code while maintaining lexical scoping, influencing compiler design and [domain-specific language](/page/Domain-specific_language) creation in [programming language theory](/page/Programming_language_theory).[](https://dl.acm.org/doi/10.1145/62678.62686)
Supporting educational efforts, tools like DrRacket provide an [integrated development environment](/page/Integrated_development_environment) tailored for beginners learning Scheme. DrRacket includes teaching languages derived from Scheme, such as Beginning Student Language, which restrict features to prevent common errors and enforce gradual progression from simple expressions to full functional constructs.[](https://docs.racket-lang.org/drracket/htdp-langs.html) This IDE offers interactive debugging, syntax checking, and visualization tools that make abstract concepts like [recursion](/page/Recursion) and environments more accessible.[](https://docs.racket-lang.org/drracket/)
As of 2025, Scheme maintains relevance in [functional programming](/page/Functional_programming) curricula at universities.
### Production and Embedded Systems
Scheme has found niche applications in production environments through its embeddability and extensibility, particularly in [open-source software](/page/Open-source_software) ecosystems. The Guile implementation serves as the official extension language for [GNU](/page/GNU) projects, enabling Scheme scripting within various tools. For instance, LilyPond, an automated music [engraving](/page/Engraving) system, integrates Guile to allow users to extend its functionality with Scheme code for custom notation and layout definitions.[](https://lilypond.org/doc/v2.24/Documentation/extending/scheme-tutorial) Guile's role extends to other [GNU](/page/GNU) applications, such as [GnuCash](/page/GnuCash) for financial data processing and scripting tasks.
In gaming and tool development, Scheme provides lightweight scripting akin to [Lua](/page/Lua), with implementations like [Gambit](/page/Gambit) enabling portable, high-performance extensions. [Gambit](/page/Gambit) powered the iOS game *Cloud Breaker* (2010), where its cooperative threading system handled game logic and audio processing in a mobile context, demonstrating Scheme's viability for real-time applications.[](https://archive.jlongster.com/Open-Sourcing-My-Gambit-Scheme-iOS-Game-from-2010)
For web development, Racket supports full-stack applications via its built-in [web server](/page/Web_server), which handles HTTP requests and generates dynamic content for server-side scripting.[](https://docs.racket-lang.org/continue/) Chicken Scheme complements this with the Spiffy library, a lightweight [web server](/page/Web_server) that includes CGI handlers for deploying Scheme scripts in traditional web environments.[](http://wiki.call-cc.org/egg/spiffy-cgi-handlers)
Embedded deployments leverage Scheme's compact runtimes for resource-limited systems. Chibi-Scheme, a minimal library with no external dependencies, is optimized for integration into C applications, making it suitable for IoT devices where footprint and embedding ease are critical.[](https://synthcode.com/scheme/chibi/) Gambit Scheme extends to mobile and embedded scenarios, supporting cross-platform development through frameworks like LambdaNative, which targets iOS, Android, and web assembly for production tools.[](http://gambitscheme.org/) In 2025, scheme-rs emerged as a specialized implementation for asynchronous Rust ecosystems, providing seamless interop to embed Scheme in high-concurrency applications like networked services.
Notable production uses include Bigloo Scheme in server-side contexts via the Hop platform, which compiles Scheme to efficient executables for blending server and client logic in web applications.[](https://www.call-with.cc/post/state-of-scheme-to-javascript) These examples illustrate transitions from research prototypes—such as early [Gambit](/page/Gambit) experiments in portable interpreters—to mature deployments in tools like [LilyPond](/page/LilyPond).[](http://www.gambitscheme.org/research/)
Despite these successes, Scheme in production encounters challenges in [performance tuning](/page/Performance_tuning), often necessitating [ahead-of-time compilation](/page/Ahead-of-time_compilation) to C or JVM backends to match native speeds in compute-intensive tasks.[](https://www-sop.inria.fr/indes/fp/Bigloo/) Additionally, its library ecosystem remains smaller and more fragmented than those of Python or [Java](/page/Java), requiring developers to build or adapt extensions for broader integration, though community efforts like SRFIs address standardization gaps.[](https://jointhefreeworld.org/blog/articles/lisps/scheme-and-lisps-are-great-for-production/index.html)
References
- https://en.wikisource.org/wiki/Scheme:_An_Interpreter_for_Extended_Lambda_Calculus/Whole_text