This document describes version 0.1a5 of Nausicaa/Scheme, a set of Scheme libraries defining a slightly modified R6RS Scheme language and augmenting the features of the base and standard R6RS libraries.
The project home page of Nausicaa is at:
development of Nausicaa takes place at:
Copyright © 2008, 2009 by Marco Maggi.
Copyright © 1996, 1997, 2000, 2001, 2002, 2003, 2004, 2005
Free Software Foundation.
Copyright © 1996, 1999-2005 Dorai Sitaram.
Copyright © 1998 Oleg Kiselyov.
Copyright © 1998, 1999, 2000 Olin Shivers.
Copyright © 1999 John David Stone.
Copyright © 1999, 2002 Marc Feeley.
Copyright © 2001, 2009 Danny Dube'
Copyright © 2002 Dr. Mirko Luedde.
Copyright © 2002, 2003, 2005, 2006 Sebastian Egner.
Copyright © 2003 Ray Dillinger.
Copyright © 2003 Taylor Campbell.
Copyright © 2005 Jens Axel Soegaard.
Copyright © 2005-2009 Alex Shinn.
Copyright © 2008 Taro Minowa (Higepon).
Copyright © 2005-2008 Dominique Boucher.
Copyright © 2004, 2005 Tony Garnock-Jones
Copyright © 2005 LShift Ltd.
Copyright © 2007, 2008 Philip L. Bewig.
Copyright © 2000 Will Fitzgerald.
Copyright © 2000 Neodesic Corporation.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections being “GNU Free Documentation License” and “GNU General Public License”, no Front–Cover Texts, and no Back–Cover Texts. A copy of the license is included in the section entitled “GNU Free Documentation License”.
This document embeds an unofficial assemblage of several documents reformatted in Texinfo; the reformatting author and maintainer is Marco Maggi marco.maggi-ipsu@gna.org. See the appendix “Credits” for the list of original documents and their authors. See also the README and CREDITS files for additional attributions.
More libraries
Low level libraries
Appendices
Extenal documents
Indexes
--- The Detailed Node Listing ---
Overview of the distribution
The Scheme language
Overview of Scheme
Numbers
Lexical syntax and datum syntax
Lexical syntax
Datum syntax
Semantic concepts
Entry format
Libraries
Library form
Top--level programs
Primitive syntax
Base library
Definitions
Expressions
Mathematics
Semantics of common operations
Numerical operations
Standard libraries
Unicode
Bytevectors
Records
Exceptions and conditions
Conditions
Input/output
Port input/output
Arithmetics
Syntax--case
Examples
Hashtables
The (nausicaa) language
Predefined condition types
List library
Fold, unfold and map
Character sets
Predefined character sets
Strings library
Comparison
Vectors library
Fold and unfold
Multidimensional arrays
Streams
Examples
Comparison functions
Loop constructs
Comprehensions
Generators
Sources of random bits
Notes on the algorithms
Utilities
Auxiliary generators
Time data types and procedures
Formatting strings
Escape sequences for flonums
Lightweight testing
Simple queue
Simple queue
Compensation stacks
Deferred exceptions
Record utilities
Conventional type descriptors
Binding accessors and mutators
Multimethod dispatching
Extending records
Builtin extensions.
A simple object system
Classes
Generic functions and methods
Handling symbolic expressions
Pattern matching
The pattern syntax
IrRegular expressions
Extended SRE Syntax
Portable regular expressions for Scheme
The regexp pattern language
Lexer and parser utilities
Source location records
Lexical token records
A lexical analyser generator
Syntax of the specification
Semantics of the specification
A LALR(1) parser generator
Introduction to the LR parser
Defining the parser
Error recovery
Dummy examples of grammar definitions
Packrat parser
Introduction to the algorithm
Parser state and results
Combinators
Comma--separated value (CSV) utilities
Low level tokenisation
Email processing
Parsing email addresses
Record types
Infix notation utilities
Parsing command line arguments
Knuth-Morris-Pratt searching
One dimensional extended ranges
Miscellaneous notes on everything
How to deal with not--a--number
Launching programs directly
Scheme programming examples
Exploring macro mechanisms
Syntax objects and you
Nausicaa/Scheme is a set of Scheme libraries defining a slightly
modified R6RS Scheme language and augmenting the features of the
base and standard R6RS libraries. To use it when writing a library
or program, we import (nausicaa) in place of (rnrs):
(import (nausicaa)
---)
the only way to use the (nausicaa) library is as language
definition, if we try to import both (nausicaa) and (rnrs)
conflicts may arise.
Nausicaa/Scheme supports the following Scheme implementations:
when referring to all of these we can use the LIMY abbreviation.
Some Scheme implementation defines itself as R6RS compliant, but then does not provide a compliant implementation of some of the features. Also, the R6RS language has some limitations and underspecifications.
Nausicaa/Scheme tries, through the (nausicaa) language, to fill
some (not all) of the gaps and to provide a base language to write
portable programs among the supported Scheme implementations.
(nausicaa) has the following purposes:
(rnrs).
(nausicaa) tries to provide a working replacement.
This document describes the features of the R6RS language by embedding the original R6RS documents; these specifications are available in the chapters:
the features of (nausicaa) that extend the (rnrs) language
are described in the chapter The augmented language.
In addition to the focus on code portability among R6RS Scheme implementations, the Nausicaa distribution is concerned to provide a good base of features and interfaces to foreign libraries (mostly coded in the C language).
With these purposes, Nausicaa/Scheme installs libraries implementing, reimplementing or extending some of the SRFIs that were voted to final status, and providing a unified API to the foreign functions interfaces of the underlying implementations.
The appendix Launching programs directly describes an
optional feature of Nausicaa/Scheme that activates Scheme programs
execution through the mechanism of the binfmt_misc Linux kernel
module. It is available only on Unix–like platforms running the Linux
kernel.
This chapter gives an overview of Scheme's semantics. The purpose of this overview is to explain enough about the basic concepts of the language to facilitate understanding of the subsequent chapters of the report, which are organized as a reference manual. Consequently, this overview is neither a complete introduction to the language, nor precise in all respects, nor normative in any way.
Following Algol, Scheme is a statically scoped programming language. Each use of a variable is associated with a lexically apparent binding of that variable.
Scheme has latent as opposed to manifest types. Types are associated with objects (also called values) rather than with variables. (Some authors refer to languages with latent types as untyped, weakly typed or dynamically typed languages.) Other languages with latent types are Python, Ruby, Smalltalk, and other dialects of Lisp. Languages with manifest types (sometimes referred to as strongly typed or statically typed languages) include Algol 60, C, C#, Java, Haskell, and ML.
All objects created in the course of a Scheme computation, including procedures and continuations, have unlimited extent. No Scheme object is ever destroyed. The reason that implementations of Scheme do not (usually!) run out of storage is that they are permitted to reclaim the storage occupied by an object if they can prove that the object cannot possibly matter to any future computation. Other languages in which most objects have unlimited extent include C#, Java, Haskell, most Lisp dialects, ML, Python, Ruby, and Smalltalk.
Implementations of Scheme must be properly tail–recursive. This allows the execution of an iterative computation in constant space, even if the iterative computation is described by a syntactically recursive procedure. Thus with a properly tail–recursive implementation, iteration can be expressed using the ordinary procedure–call mechanics, so that special iteration constructs are useful only as syntactic sugar.
Scheme was one of the first languages to support procedures as objects in their own right. Procedures can be created dynamically, stored in data structures, returned as results of procedures, and so on. Other languages with these properties include Common Lisp, Haskell, ML, Ruby, and Smalltalk.
One distinguishing feature of Scheme is that continuations, which in most other languages only operate behind the scenes, also have “first–class” status. First–class continuations are useful for implementing a wide variety of advanced control constructs, including non–local exits, backtracking, and coroutines.
In Scheme, the argument expressions of a procedure call are evaluated before the procedure gains control, whether the procedure needs the result of the evaluation or not. C, C#, Common Lisp, Python, Ruby, and Smalltalk are other languages that always evaluate argument expressions before invoking a procedure. This is distinct from the lazy–evaluation semantics of Haskell, or the call–by–name semantics of Algol 60, where an argument expression is not evaluated unless its value is needed by the procedure.
Scheme's model of arithmetic provides a rich set of numerical types and operations on them. Furthermore, it distinguishes exact and inexact number objects: Essentially, an exact number object corresponds to a number exactly, and an inexact number object is the result of a computation that involved rounding or other errors.
Scheme programs manipulate objects, which are also referred to as values. Scheme objects are organized into sets of values called types. This section gives an overview of the fundamentally important types of the Scheme language. More types are described in later chapters.
NOTE As Scheme is latently typed, the use of the term type in this report differs from the use of the term in the context of other languages, particularly those with manifest typing.
A boolean is a truth value, and can be either true or false. In Scheme,
the object for “false” is written #f. The object for “true”
is written #t. In most places where a truth value is expected,
however, any object different from #f counts as true.
Scheme supports a rich variety of numerical data types, including objects representing integers of arbitrary precision, rational numbers, complex numbers, and inexact numbers of various kinds. Numbers
Scheme characters mostly correspond to textual characters. More precisely, they are isomorphic to the scalar values of the Unicode standard.
Strings are finite sequences of characters with fixed length and thus represent arbitrary Unicode texts.
A symbol is an object representing a string, the symbol's name. Unlike strings, two symbols whose names are spelled the same way are never distinguishable. Symbols are useful for many applications; for instance, they may be used the way enumerated values are used in other languages.
A pair is a data structure with two components. The most common use of pairs is to represent (singly linked) lists, where the first component (the car) represents the first element of the list, and the second component (the cdr) the rest of the list. Scheme also has a distinguished empty list, which is the last cdr in a chain of pairs that form a list.
Vectors, like lists, are linear data structures representing finite sequences of arbitrary objects. Whereas the elements of a list are accessed sequentially through the chain of pairs representing it, the elements of a vector are addressed by integer indices. Thus, vectors are more appropriate than lists for random access to elements.
Procedures are values in Scheme.
The most important elements of Scheme code are expressions. Expressions can be evaluated, producing a value. (Actually, any number of values. Multiple return values.)
The most fundamental expressions are literal expressions:
#t ⇒ #t
23 ⇒ 23
this notation means that the expression ‘#t’ evaluates to #t,
that is, the value for “true”, and that the expression ‘23’
evaluates to a number object representing the number 23.
Compound expressions are formed by placing parentheses around their subexpressions. The first subexpression identifies an operation; the remaining subexpressions are operands to the operation:
(+ 23 42) ⇒ 65
(+ 14 (* 23 42)) ⇒ 980
in the first of these examples, + is the name of the built–in
operation for addition, and ‘23’ and ‘42’ are the operands.
The expression (+ 23 42) reads as “the sum of 23 and
42”. Compound expressions can be nested—the second example
reads as “the sum of 14 and the product of 23 and
42”.
As these examples indicate, compound expressions in Scheme are always written using the same prefix notation. As a consequence, the parentheses are needed to indicate structure. Consequently, “superfluous” parentheses, which are often permissible in mathematical notation and also in many programming languages, are not allowed in Scheme.
As in many other languages, whitespace (including line endings) is not significant when it separates subexpressions of an expression, and can be used to indicate structure.
Scheme allows identifiers to stand for locations containing values. These identifiers are called variables. In many cases, specifically when the location's value is never modified after its creation, it is useful to think of the variable as standing for the value directly.
(let ((x 23)
(y 42))
(+ x y))
⇒ 65
In this case, the expression starting with let is a binding
construct. The parenthesized structure following the let lists
variables alongside expressions: the variable ‘x’ alongside
‘23’, and the variable ‘y’ alongside ‘42’. The
let expression binds ‘x’ to ‘23’, and ‘y’ to
‘42’. These bindings are available in the body of the
let expression, (+ x y), and only there.
The variables bound by a let expression are local, because
their bindings are visible only in let's body. Scheme also
allows creating top–level bindings for identifiers as follows:
(define x 23)
(define y 42)
(+ x y) ⇒ 65
these are actually “top–level” in the body of a top–level program or library. Libraries.
The first two parenthesized structures are definitions; they create top–level bindings, binding ‘x’ to ‘23’ and ‘y’ to ‘42’. Definitions are not expressions, and cannot appear in all places where an expression can occur. Moreover, a definition has no value.
Bindings follow the lexical structure of the program: When several bindings with the same name exist, a variable refers to the binding that is “closest” to it, starting with its occurrence in the program and going from inside to outside, and referring to a top–level binding if no local binding can be found along the way:
(define x 23)
(define y 42)
(let ((y 43))
(+ x y)) ⇒ 66
(let ((y 43))
(let ((y 44))
(+ x y))) ⇒ 67
While definitions are not expressions, compound expressions and definitions exhibit similar syntactic structure:
(define x 23)
(* x 2)
While the first line contains a definition, and the second an
expression, this distinction depends on the bindings for let and
*. At the purely syntactical level, both are forms, and
form is the general name for a syntactic part of a Scheme program.
In particular, ‘23’ is a subform of the form (define x
23).
Definitions can also be used to define procedures:
(define (f x)
(+ x 42))
(f 23) ⇒ 65
A procedure is, slightly simplified, an abstraction of an expression
over objects. In the example, the first definition defines a procedure
called f. (Note the parentheses around f x, which
indicate that this is a procedure definition.) The expression (f
23) is a procedure call, meaning, roughly, “evaluate (+ x 42)
(the body of the procedure) with ‘x’ bound to ‘23’”.
As procedures are objects, they can be passed to other procedures:
(define (f x)
(+ x 42))
(define (g p x)
(p x))
(g f 23) ⇒ 65
In this example, the body of g is evaluated with p bound to
f and ‘x’ bound to ‘23’, which is equivalent to
(f 23), which evaluates to ‘65’.
In fact, many predefined operations of Scheme are provided not by
syntax, but by variables whose values are procedures. The +
operation, for example, which receives special syntactic treatment in
many other languages, is just a regular identifier in Scheme, bound to a
procedure that adds number objects. The same holds for * and
many others:
(define (h op x y)
(op x y))
(h + 23 42) ⇒ 65
(h * 23 42) ⇒ 966
Procedure definitions are not the only way to create procedures. A
lambda expression creates a new procedure as an object, with no
need to specify a name:
((lambda (x) (+ x 42)) 23) ⇒ 65
The entire expression in this example is a procedure call; (lambda
(x) (+ x 42)), evaluates to a procedure that takes a single number
object and adds 42 to it.
Whereas (+ 23 42), (f 23), and ((lambda (x) (+ x
42)) 23) are all examples of procedure calls, lambda and
let expressions are not. This is because let, even though
it is an identifier, is not a variable, but is instead a syntactic
keyword. A form that has a syntactic keyword as its first
subexpression obeys special rules determined by the keyword. The
let identifier in a definition is also a syntactic keyword.
Hence, definitions are also not procedure calls.
The rules for the lambda keyword specify that the first subform
is a list of parameters, and the remaining subforms are the body of the
procedure. In let expressions, the first subform is a list of
binding specifications, and the remaining subforms constitute a body of
expressions.
Procedure calls can generally be distinguished from these special forms by looking for a syntactic keyword in the first position of a form: if the first position does not contain a syntactic keyword, the expression is a procedure call. (So–called identifier macros allow creating other kinds of special forms, but are comparatively rare.) The set of syntactic keywords of Scheme is fairly small, which usually makes this task fairly simple. It is possible, however, to create new bindings for syntactic keywords. Derived forms and macros
Scheme variables bound by definitions or let or lambda
expressions are not actually bound directly to the objects specified in
the respective bindings, but to locations containing these objects. The
contents of these locations can subsequently be modified destructively
via assignment:
(let ((x 23))
(set! x 42)
x) ⇒ 42
In this case, the body of the let expression consists of two
expressions which are evaluated sequentially, with the value of the
final expression becoming the value of the entire let expression.
The expression (set! x 42) is an assignment, saying “replace the
object in the location referenced by ‘x’ with ‘42’”. Thus,
the previous value of ‘x’, ‘23’, is replaced by ‘42’.
Many of the special forms specified in this report can be translated
into more basic special forms. For example, a let expression can
be translated into a procedure call and a lambda expression. The
following two expressions are equivalent:
(let ((x 23)
(y 42))
(+ x y))
⇒ 65
((lambda (x y) (+ x y)) 23 42)
⇒ 65
Special forms like let expressions are called derived forms
because their semantics can be derived from that of other kinds of forms
by a syntactic transformation. Some procedure definitions are also
derived forms. The following two definitions are equivalent:
(define (f x)
(+ x 42))
(define f
(lambda (x)
(+ x 42)))
In Scheme, it is possible for a program to create its own derived forms by binding syntactic keywords to macros:
(define-syntax def
(syntax-rules ()
((def f (p ...) body)
(define (f p ...)
body))))
(def f (x)
(+ x 42))
The define-syntax construct specifies that a parenthesized
structure matching the pattern (def f (p ...) body), where
‘f’, ‘p’, and ‘body’ are pattern variables, is translated
to (define (f p ...) body). Thus, the ‘def’ form appearing
in the example gets translated to:
(define (f x)
(+ x 42))
The ability to create new syntactic keywords makes Scheme extremely flexible and expressive, allowing many of the features built into other languages to be derived forms in Scheme.
A subset of the Scheme objects is called datum values. These include booleans, number objects, characters, symbols, and strings as well as lists and vectors whose elements are data. Each datum value may be represented in textual form as a syntactic datum, which can be written out and read back in without loss of information. A datum value may be represented by several different syntactic data. Moreover, each datum value can be trivially translated to a literal expression in a program by prepending a ‘'’ (single quote) to a corresponding syntactic datum:
'23 ⇒ 23
'#t ⇒ #t
'foo ⇒ foo
'(1 2 3) ⇒ (1 2 3)
'#(1 2 3) ⇒ #(1 2 3)
The ‘'’ shown in the previous examples is not needed for representations of number objects or booleans. The syntactic datum ‘foo’ represents a symbol with name “foo”, and ‘'foo’ is a literal expression with that symbol as its value. ‘(1 2 3)’ is a syntactic datum that represents a list with elements ‘1’, ‘2’, and ‘3’, and ‘'(1 2 3)’ is a literal expression with this list as its value. Likewise, ‘#(1 2 3)’ is a syntactic datum that represents a vector with elements ‘1’, ‘2’ and ‘3’, and ‘'#(1 2 3)’ is the corresponding literal.
The syntactic data are a superset of the Scheme forms. Thus, data can be used to represent Scheme forms as data objects. In particular, symbols can be used to represent identifiers.
'(+ 23 42) ⇒ (+ 23 42)
'(define (f x) (+ x 42)) ⇒ (define (f x) (+ x 42))
This facilitates writing programs that operate on Scheme source code, in particular interpreters and program transformers.
Whenever a Scheme expression is evaluated there is a continuation
wanting the result of the expression. The continuation represents an
entire (default) future for the computation. For example, informally
the continuation of 3 in the expression
(+ 1 3)
adds 1 to it. Normally these ubiquitous continuations are hidden behind the scenes and programmers do not think much about them. On rare occasions, however, a programmer may need to deal with continuations explicitly.
The call-with-current-continuation procedure allows Scheme
programmers to do that by creating a procedure that reinstates the
current continuation. Control features
The call-with-current-continuation procedure accepts a procedure,
calls it immediately with an argument that is an escape
procedure. This escape procedure can then be called with an argument
that becomes the result of the call to
call-with-current-continuation. That is, the escape procedure
abandons its own continuation, and reinstates the continuation of the
call to call-with-current-continuation.
In the following example, an escape procedure representing the
continuation that adds ‘1’ to its argument is bound to
escape, and then called with ‘3’ as an argument. The
continuation of the call to escape is abandoned, and instead the
‘3’ is passed to the continuation that adds ‘1’:
(+ 1 (call-with-current-continuation
(lambda (escape)
(+ 2 (escape 3)))))
⇒ 4
An escape procedure has unlimited extent: It can be called after the
continuation it captured has been invoked, and it can be called multiple
times. This makes call-with-current-continuation significantly
more powerful than typical non–local control constructs such as
exceptions in other languages.
Scheme code can be organized in components called libraries. Each library contains definitions and expressions. It can import definitions from other libraries and export definitions to other libraries.
The following library called ‘(hello)’ exports a definition called
hello-world, and imports the base library and the simple I/O
library. The hello-world export is a procedure that displays
‘Hello World’ on a separate line:
(library (hello)
(export hello-world)
(import (rnrs base)
(rnrs io simple))
(define (hello-world)
(display "Hello World")
(newline)))
A Scheme program is invoked via a top–level program. Like a library, a top–level program contains imports, definitions and expressions, and specifies an entry point for execution. Thus a top–level program defines, via the transitive closure of the libraries it imports, a Scheme program.
The following top–level program obtains the first argument from the
command line via the command-line procedure from the
(rnrs programs (6)) library. It then opens the file using
open-file-input-port, yielding a port, i.e. a connection
to the file as a data source, and calls the get-bytes-all
procedure to obtain the contents of the file as binary data. It then
uses put-bytes to output the contents of the file to standard
output:
#!r6rs
(import (rnrs base)
(rnrs io ports)
(rnrs programs))
(let ((p (standard-output-port)))
(put-bytevector p
(call-with-port
(open-file-input-port
(cadr (command-line)))
get-bytevector-all))
(close-port p))
The key words “must”, “must not”, “should”, “should not”, “recommended”, “may”, and “optional” in this report are to be interpreted as described in RFC 2119. Specifically:
In particular, this report occasionally uses “should” to designate circumstances that are outside the specification of this report, but cannot be practically detected by an implementation. Arguments checking. In such circumstances, a particular implementation may allow the programmer to ignore the recommendation of the report and even exhibit reasonable behavior. However, as the report does not specify the behavior, these programs may be unportable, that is, their execution might produce different results on different implementations.
Moreover, this report occasionally uses the phrase “not required” to note the absence of an absolute requirement.
This chapter describes Scheme's model for numbers. It is important to distinguish between the mathematical numbers, the Scheme objects that attempt to model them, the machine representations used to implement the numbers, and notations used to write numbers.
In this report, the term number refers to a mathematical number, and the term number object refers to a Scheme object representing a number. This report uses the types complex, real, rational, and integer to refer to both mathematical numbers and number objects. The fixnum and flonum types refer to special subsets of the number objects, as determined by common machine representations, as explained below.
Numbers may be arranged into a tower of subsets in which each level is a subset of the level above it:
number
complex
real
rational
integer
For example, 5 is an integer. Therefore 5 is also a rational, a real, and a complex. The same is true of the number objects that model 5.
Number objects are organized as a corresponding tower of subtypes
defined by the predicates number?, complex?, real?,
rational?, and integer?. Numerical types predicates. Integer number objects are also
called integer objects.
There is no simple relationship between the subset that contains a number and its representation inside a computer. For example, the integer 5 may have several representations. Scheme's numerical operations treat number objects as abstract data, as independent of their representation as possible. Although an implementation of Scheme may use many different representations for numbers, this should not be apparent to a casual programmer writing simple programs.
It is useful to distinguish between number objects that are known to correspond to a number exactly, and those number objects whose computation involved rounding or other errors. For example, index operations into data structures may need to know the index exactly, as may some operations on polynomial coefficients in a symbolic algebra system. On the other hand, the results of measurements are inherently inexact, and irrational numbers may be approximated by rational and therefore inexact approximations. In order to catch uses of numbers known only inexactly where exact numbers are required, Scheme explicitly distinguishes exact from inexact number objects. This distinction is orthogonal to the dimension of type.
A number object is exact if it is the value of an exact numerical literal or was derived from exact number objects using only exact operations. Exact number objects correspond to mathematical numbers in the obvious way.
Conversely, a number object is inexact if it is the value of an inexact numerical literal, or was derived from inexact number objects, or was derived using inexact operations. Thus inexactness is contagious.
Exact arithmetic is reliable in the following sense: If exact number objects are passed to any of the arithmetic procedures described in Propagation of exactness and inexactness, and an exact number object is returned, then the result is mathematically correct. This is generally not true of computations involving inexact number objects because approximate methods such as floating–point arithmetics may be used, but it is the duty of each implementation to make the result as close as practical to the mathematically ideal result.
A fixnum is an exact integer object that lies within a certain implementation–dependent subrange of the exact integer objects. Fixnums
Likewise, every implementation must designate a subset of its inexact real number objects as flonums, and to convert certain external representations into flonums. Flonums
Note that this does not imply that an implementation must use floating–point representations.
Implementations of Scheme must support number objects for the entire tower of subtypes given in Numerical tower. Moreover, implementations must support exact integer objects and exact rational number objects of practically unlimited size and precision, and to implement certain procedures (listed in Propagation of exactness and inexactness), so they always return exact results when given exact arguments. (“Practically unlimited” means that the size and precision of these numbers should only be limited by the size of the available memory.)
Implementations may support only a limited range of inexact number objects of any type, subject to the requirements of this section. For example, an implementation may limit the range of the inexact real number objects (and therefore the range of inexact integer and rational number objects) to the dynamic range of the flonum format. Furthermore the gaps between the inexact integer objects and rationals are likely to be very large in such an implementation as the limits of this range are approached.
An implementation may use floating point and other approximate representation strategies for inexact numbers. This report recommends, but does not require, that the IEEE floating–point standards be followed by implementations that use floating–point representations, and that implementations using other representations should match or exceed the precision achievable using these floating–point standards.
In particular, implementations that use floating–point representations
must follow these rules: A floating–point result must be represented
with at least as much precision as is used to express any of the inexact
arguments to that operation. Potentially inexact operations such as
sqrt, when applied to exact arguments, should produce exact
answers whenever possible (for example the square root of an exact 4
ought to be an exact 2). However, this is not required. If, on the
other hand, an exact number object is operated upon so as to produce an
inexact result (as by sqrt), and if the result is represented in
floating point, then the most precise floating–point format available
must be used; but if the result is represented in some other way then
the representation must have at least as much precision as the most
precise floating–point format available.
It is the programmer's responsibility to avoid using inexact number objects with magnitude or significand too large to be represented in the implementation.
Some Scheme implementations, specifically those that follow the IEEE floating–point standards, distinguish special number objects called positive infinity, negative infinity and NaN.
Positive infinity is regarded as an inexact real (but not rational) number object that represents an indeterminate number greater than the numbers represented by all rational number objects. Negative infinity is regarded as an inexact real (but not rational) number object that represents an indeterminate number less than the numbers represented by all rational numbers.
A NaN is regarded as an inexact real (but not rational) number object so indeterminate that it might represent any real number, including positive or negative infinity, and might even be greater than positive infinity or less than negative infinity.
Some Scheme implementations, specifically those that follow the IEEE floating–point standards, distinguish between number objects for 0.0 and -0.0, i.e., positive and negative inexact zero. This report will sometimes specify the behavior of certain arithmetic operations on these number objects. These specifications are marked with “if -0.0 is distinguished” or “implementations that distinguish -0.0”.
The syntax of Scheme code is organized in three levels:
Syntactic data (also called external representations) double as a
notation for objects, and Scheme's (rnrs io ports (6)) library
provides the get-datum and put-datum procedures for
reading and writing syntactic data, converting between their textual
representation and the corresponding objects. Port input/output. Each syntactic datum represents a corresponding
datum value. A syntactic datum can be used in a program to
obtain the corresponding datum value using quote. Quotation
Scheme source code consists of syntactic data and (non–significant) comments. Syntactic data in Scheme source code are called forms. (A form nested inside another form is called a subform.) Consequently, Scheme's syntax has the property that any sequence of characters that is a form is also a syntactic datum representing some object. This can lead to confusion, since it may not be obvious out of context whether a given sequence of characters is intended to be a representation of objects or the text of a program. It is also a source of power, since it facilitates writing programs such as interpreters or compilers that treat programs as objects (or vice versa).
A datum value may have several different external representations. For
example, both #e28.000 and #x1c are syntactic data
representing the exact integer object 28, and the syntactic data
(8 13), ( 08 13 ), (8 . (13 . ())) all represent a
list containing the exact integer objects 8 and 13. Syntactic data that
represent equal objects (in the sense of equal?; baselib predicates) are always equivalent as forms of a program.
Because of the close correspondence between syntactic data and datum values, this report sometimes uses the term datum for either a syntactic datum or a datum value when the exact meaning is apparent from the context.
An implementation must not extend the lexical or datum syntax in any
way, with one exception: it need not treat the syntax
#!<identifier>, for any <identifier> (lang lex syntax identifiers) that is not r6rs, as a syntax violation, and it may
use specific #!–prefixed identifiers as flags indicating that
subsequent input contains extensions to the standard lexical or datum
syntax. The syntax #!r6rs may be used to signify that the input
afterward is written with the lexical syntax and datum syntax described
by this report. #!r6rs is otherwise treated as a comment;
lang lex syntax whitespace and comments.
The formal syntax for Scheme is written in an extended BNF. Non–terminals are written using angle brackets. Case is insignificant for non–terminal names.
All spaces in the grammar are for legibility. <empty> stands for the empty string.
The following extensions to BNF are used to make the description more concise: <thing>* means zero or more occurrences of <thing>, and <thing>+ means at least one <thing>.
Some non-terminal names refer to the Unicode scalar values of the same name: <character tabulation> (U+0009), <linefeed> (U+000A), <carriage return> (U+000D), <line tabulation> (U+000B), <form feed> (U+000C), <space> (U+0020), <next line> (U+0085), <line separator> (U+2028), and <paragraph separator> (U+2029).
The lexical syntax determines how a character sequence is split into a sequence of lexemes, omitting non–significant portions such as comments and whitespace. The character sequence is assumed to be text according to the Unicode standard. Some of the lexemes, such as identifiers, representations of number objects, strings etc., of the lexical syntax are syntactic data in the datum syntax, and thus represent objects. Besides the formal account of the syntax, this section also describes what datum values are represented by these syntactic data.
The lexical syntax, in the description of comments, contains a forward reference to <datum>, which is described as part of the datum syntax. Being comments, however, these <datum>s do not play a significant role in the syntax.
Case is significant except in representations of booleans, number
objects, and in hexadecimal numbers specifying Unicode scalar values.
For example, #x1A and #X1a are equivalent. The identifier
Foo is, however, distinct from the identifier FOO.
<interlexeme space> may occur on either side of any lexeme, but not within a lexeme.
<Identifier>s, ‘.’, <number>s, <character>s, and <boolean>s, must be terminated by a <delimiter> or by the end of the input.
The following two characters are reserved for future extensions to the
language: { }
<lexeme> -> <identifier> | <boolean> | <number>
| <character> | <string>
| ( | ) | [ | ] | #( | #vu8( | ' | ` | , | ,@ | .
| #' | #` | #, | #,@
<delimiter> -> ( | ) | [ | ] | " | ; | #
| <whitespace>
<whitespace> -> <character tabulation>
| <linefeed> | <line tabulation> | <form feed>
| <carriage return> | <next line>
| <any character whose category is Zs, Zl, or Zp>
<line ending> -> <linefeed> | <carriage return>
| <carriage return> <linefeed> | <next line>
| <carriage return> <next line> | <line separator>
<comment> -> ; <all subsequent characters up to a <line ending>
or <paragraph separator> >
| <nested comment>
| #; <interlexeme space> <datum>
| #!r6rs
<nested comment> -> #| <comment text>
<comment cont>* |#
<comment text> -> character sequence not containing #| or |#
<comment cont> -> <nested comment> <comment text>
<atmosphere> -> <whitespace> | <comment>
<interlexeme space> -> <atmosphere>*
<identifier> -> <initial> <subsequent>*
| <peculiar identifier>
<initial> -> <constituent> | <special initial>
| <inline hex escape>
<letter> -> a | b | c | ... | z
| A | B | C | ... | Z
<constituent> -> <letter>
| <any character whose Unicode scalar value is greater than
127, and whose category is Lu, Ll, Lt, Lm, Lo, Mn,
Nl, No, Pd, Pc, Po, Sc, Sm, Sk, So, or Co>
<special initial> -> ! | $ | % | & | * | / | : | < | =
| > | ? | ^ | _ | ~
<subsequent> -> <initial> | <digit>
| <any character whose category is Nd, Mc, or Me>
| <special subsequent>
<digit> -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
<hex digit> -> <digit>
| a | A | b | B | c | C | d | D | e | E | f | F
<special subsequent> -> + | - | . | @
<inline hex escape> -> \x<hex scalar value>;
<hex scalar value> -> <hex digit>+
<peculiar identifier> -> + | - | ... | -> <subsequent>*
<boolean> -> #t | #T | #f | #F
<character> -> #\<any character>
| #\<character name>
| #\x<hex scalar value>
<character name> -> nul | alarm | backspace | tab
| linefeed | newline | vtab | page | return
| esc | space | delete
<string> -> " <string element>* "
<string element> -> <any character other than " or \>
| \a | \b | \t | \n | \v | \f | \r
| \" | \\
| \<intraline whitespace>* <line ending>
<intraline whitespace>*
| <inline hex escape>
<intraline whitespace> -> <character tabulation>
| <any character whose category is Zs>
A <hex scalar value> represents a Unicode scalar value between
0 and #x10FFFF, excluding the range [#D800,
#xDFFF].
The rules for <num R>, <complex R>, <real R>, <ureal R>, <uinteger R>, and <prefix R> below should be replicated for R = 2, 8, 10, and 16. There are no rules for <decimal 2>, <decimal 8>, and <decimal 16>, which means that number representations containing decimal points or exponents must be in decimal radix.
<number> -> <num 2> | <num 8>
| <num 10> | <num 16>
<num R> -> <prefix R> <complex R>
<complex R> -> <real R> | <real R> @ <real R>
| <real R> + <ureal R> i | <real R> - <ureal R> i
| <real R> + <naninf> i | <real R> - <naninf> i
| <real R> + i | <real R> - i
| + <ureal R> i | - <ureal R> i
| + <naninf> i | - <naninf> i
| + i | - i
<real R> -> <sign> <ureal R>
| + <naninf> | - <naninf>
<naninf> -> nan.0 | inf.0
<ureal R> -> <uinteger R>
| <uinteger R> / <uinteger R>
| <decimal R> <mantissa width>
<decimal 10> -> <uinteger 10> <suffix>
| . <digit 10>+ <suffix>
| <digit 10>+ . <digit 10>* <suffix>
| <digit 10>+ . <suffix>
<uinteger R> -> <digit R>+
<prefix R> -> <radix R> <exactness>
| <exactness> <radix R>
<suffix> -> <empty>
| <exponent marker> <sign> <digit 10>+
<exponent marker> -> e | E | s | S | f | F
| d | D | l | L
<mantissa width> -> <empty>
| | <digit 10>+
<sign> -> <empty> | + | -
<exactness> -> <empty>
| #i| #I | #e| #E
<radix 2> -> #b| #B
<radix 8> -> #o| #O
<radix 10> -> <empty> | #d | #D
<radix 16> -> #x| #X
<digit 2> -> 0 | 1
<digit 8> -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
<digit 10> -> <digit>
<digit 16> -> <hex digit>
Line endings are significant in Scheme in single–line comments (lang lex syntax whitespace and comments) and within string literals. In Scheme source code, any of the line endings in <line ending> marks the end of a line. Moreover, the two–character line endings <carriage return> <linefeed> and <carriage return> <next line> each count as a single line ending.
In a string literal, a <line ending> not preceded by a \
stands for a linefeed character, which is the standard line–ending
character of Scheme.
Whitespace characters are spaces, linefeeds, carriage returns, character tabulations, form feeds, line tabulations, and any other character whose category is Zs, Zl, or Zp. Whitespace is used for improved readability and as necessary to separate lexemes from each other. Whitespace may occur between any two lexemes, but not within a lexeme. Whitespace may also occur inside a string, where it is significant.
The lexical syntax includes several comment forms. In all cases, comments are invisible to Scheme, except that they act as delimiters, so, for example, a comment cannot appear in the middle of an identifier or representation of a number object.
A semicolon (;) indicates the start of a line comment. The
comment continues to the end of the line on which the semicolon appears.
Another way to indicate a comment is to prefix a <datum> (cf.
section “Formal account”) with #;, possibly with
<interlexeme space> before the <datum>. The comment consists
of the comment prefix #; and the <datum> together. This
notation is useful for “commenting out” sections of code.
Block comments may be indicated with properly nested #| and
|# pairs.
#|
The FACT procedure computes the factorial of a
non-negative integer.
|#
(define fact
(lambda (n)
;; base case
(if (= n 0)
#;(= n 1)
1 ; identity of *
(* n (fact (- n 1))))))
The lexeme #!r6rs, which signifies that the program text that
follows is written with the lexical and datum syntax described in this
report, is also otherwise treated as a comment.
Most identifiers allowed by other programming languages are also
acceptable to Scheme. In general, a sequence of letters, digits, and
“extended alphabetic characters” is an identifier when it begins with
a character that cannot begin a representation of a number object. In
addition, +, -, and ... are identifiers, as is a
sequence of letters, digits, and extended alphabetic characters that
begins with the two–character sequence ->. Here are some
examples of identifiers:
lambda q soup
list->vector + V17a
<= a34kTMNs ->-
the-word-recursion-has-many-meanings
Extended alphabetic characters may be used within identifiers as if they were letters. The following are extended alphabetic characters:
! $ % & * + - . / : < = > ? @ ^ _ ~
Moreover, all characters whose Unicode scalar values are greater than
127 and whose Unicode category is Lu, Ll, Lt, Lm, Lo, Mn, Mc, Me, Nd,
Nl, No, Pd, Pc, Po, Sc, Sm, Sk, So, or Co can be used within
identifiers. In addition, any character can be used within an
identifier when specified via an <inline hex escape>. For example,
the identifier H\x65;llo is the same as the identifier
Hello.
Any identifier may be used as a variable or as a syntactic keyword (lang basic variables keywords regions and lang syntax macros) in a Scheme program. Any identifier may also be used as a syntactic datum, in which case it represents a symbol (baselib symbols).
The standard boolean objects for true and false have external
representations #t and #f.
Characters are represented using the notation #\<character> or
#<character name> or #x<hex scalar value>.
For example:
#\a lower case letter a
#\A upper case letter A
#\( left parenthesis
#\ space character
#\nul U+0000
#\alarm U+0007
#\backspace U+0008
#\tab U+0009
#\linefeed U+000A
#\newline U+000A
#\vtab U+000B
#\page U+000C
#\return U+000D
#\esc U+001B
#\space U+0020 preferred way to write a space
#\delete U+007F
#\xFF U+00FF
#\x03BB U+03BB
#\x00006587 U+6587
#\x0001z &lexical exception
#\alarmx &lexical exception
#\alarm x U+0007 followed by x
#\Alarm &lexical exception
#\alert &lexical exception
#\xA U+000A
#\xFF U+00FF
#\xff U+00FF
#\x ff U+0078 followed by another datum, ff
#\x(ff) U+0078 followed by another datum, a parenthesized ff
#\(x) &lexical exception
#\(x &lexical exception
#\((x) U+0028 followed by another datum, parenthesized x
#\x00110000 &lexical exception out of range
#\x000000001 U+0001
#\xD800 &lexical exception in excluded range
(The notation &lexical means that the line in question is a
lexical syntax violation.)
Case is significant in #\<character>, and in #\<character
name>, but not in the <hex scalar value> of #\x<hex scalar
value>. A <character> must be followed by a <delimiter> or
by the end of the input. This rule resolves various ambiguous cases
involving named characters, requiring, for example, the sequence of
characters #\space to be interpreted as the space character
rather than as the character #\s followed by the identifier
pace.
NOTE The#\newlinenotation is retained for backward compatibility. Its use is deprecated;#\linefeedshould be used instead.
String are represented by sequences of characters enclosed within
doublequotes ("). Within a string literal, various escape
sequences represent characters other than themselves. Escape sequences
always start with a backslash (\):
\a\b\t\n\v\f\r\"\\<intraline whitespace><line ending> <intraline whitespace>\x<hex scalar value>;These escape sequences are case-sensitive, except that the alphabetic digits of a <hex scalar value> can be uppercase or lowercase.
Any other character in a string after a backslash is a syntax violation. Except for a line ending, any character outside of an escape sequence and not a doublequote stands for itself in the string literal. A line ending that does not follow a backslash stands for a linefeed character.
Examples:
"abc" U+0061, U+0062, U+0063
"\x41;bc" "Abc" ; U+0041, U+0062, U+0063
"\x41; bc" "A bc"
U+0041, U+0020, U+0062, U+0063
"\x41bc;" U+41BC
"\x41" &lexical exception
"\x;" &lexical exception
"\x41bx;" &lexical exception
"\x00000041;" "A" ; U+0041
"\x0010FFFF;" U+10FFFF
"\x00110000;" &lexical exception
out of range
"\x000000001;" U+0001
"\xD800;" &lexical exception
in excluded range
"A
bc" U+0041, U+000A, U+0062, U+0063
if no space occurs after the A
The syntax of external representations for number objects is described formally by the <number> rule in the formal grammar. Case is not significant in external representations of number objects.
A representation of a number object may be written in binary, octal,
decimal, or hexadecimal by the use of a radix prefix. The radix
prefixes are #b (binary), #o (octal), #d (decimal),
and #x (hexadecimal). With no radix prefix, a representation of
a number object is assumed to be expressed in decimal.
A representation of a number object may be specified to be either exact
or inexact by a prefix. The prefixes are #e for exact, and
#i for inexact. An exactness prefix may appear before or after
any radix prefix that is used. If the representation of a number object
has no exactness prefix, the constant is inexact if it contains a
decimal point, an exponent, or a nonempty mantissa width; otherwise it
is exact.
In systems with inexact number objects of varying precisions, it may be
useful to specify the precision of a constant. For this purpose,
representations of number objects may be written with an exponent marker
that indicates the desired precision of the inexact representation. The
letters s, f, d, and l specify the use of
short, single, double, and long precision,
respectively. (When fewer than four internal inexact representations
exist, the four size specifications are mapped onto those available.
For example, an implementation with two internal representations may map
short and single together and long and double together.) In addition,
the exponent marker e specifies the default precision for the
implementation. The default precision has at least as much precision as
double, but implementations may wish to allow this default to be
set by the user.
3.1415926535898F0
Round to single, perhaps 3.141593
0.6L0
Extend to long, perhaps .600000000000000
A representation of a number object with nonempty mantissa width,
x|p, represents the best binary floating–point approximation of
x using a p–bit significand. For example, 1.1|53
is a representation of the best approximation of 1.1 in IEEE double
precision. If x is an external representation of an inexact real
number object that contains no vertical bar, then its numerical value
should be computed as though it had a mantissa width of 53 or more.
Implementations that use binary floating–point representations of real
number objects should represent x|p using a p–bit
significand if practical, or by a greater precision if a p–bit
significand is not practical, or by the largest available precision if
p or more bits of significand are not practical within the
implementation.
Note The precision of a significand should not be confused with
the number of bits used to represent the significand. In the IEEE
floating–point standards, for example, the significand's most
significant bit is implicit in single and double precision but is
explicit in extended precision. Whether that bit is implicit or
explicit does not affect the mathematical precision. In implementations
that use binary floating point, the default precision can be calculated
by calling the following procedure:
(define (precision)
(do ([n 0 (+ n 1)]
[x 1.0 (/ x 2.0)])
((= 1.0 (+ 1.0 x)) n)))
Note When the underlying floating–point representation is IEEE double precision, the|psuffix should not always be omitted: Denormalized floating–point numbers have diminished precision, and therefore their external representations should carry a|psuffix with the actual width of the significand.
The literals +inf.0 and -inf.0 represent positive and
negative infinity, respectively. The +nan.0 literal represents
the NaN that is the result of (/ 0.0 0.0), and may represent
other NaNs as well. The -nan.0 literal also represents a NaN.
If x is an external representation of an inexact real number
object and contains no vertical bar and no exponent marker other than
e, the inexact real number object it represents is a flonum (see
library section “Flonums”). Some or all of the other external
representations of inexact real number objects may also represent
flonums, but that is not required by this report.
The datum syntax describes the syntax of syntactic data in terms of a sequence of <lexeme>s, as defined in the lexical syntax.
Syntactic data include the lexeme data described in the previous section as well as the following constructs for forming compound data:
( ) or [ ];
The following grammar describes the syntax of syntactic data in terms of various kinds of lexemes defined in the grammar in Lexical Syntax
<datum> -> <lexeme datum>
| <compound datum>
<lexeme datum> -> <boolean> | <number>
| <character> | <string> | <symbol>
<symbol> -> <identifier>
<compound datum> -> <list> | <vector> | <bytevector>
<list> -> (<datum>*) | [<datum>*]
| (<datum>+ . <datum>) | [<datum>+ . <datum>]
| <abbreviation>
<abbreviation> -> <abbrev prefix> <datum>
<abbrev prefix> -> ' | ` | , | ,@
| #' | #` | #, | #,@
<vector> -> #(<datum>*)
<bytevector> -> #vu8(<u8>*)
<u8> -> <any <number> representing an exact integer in {0, ..., 255}>
List and pair data, representing pairs and lists of values are represented using parentheses or brackets. Matching pairs of brackets that occur in the rules of <list> are equivalent to matching pairs of parentheses.
The most general notation for Scheme pairs as syntactic data is the
“dotted” notation (<datum1> . <datum2>) where <datum1> is
the representation of the value of the car field and <datum2> is
the representation of the value of the cdr field. For example (4
. 5) is a pair whose car is 4 and whose cdr is 5.
A more streamlined notation can be used for lists: the elements of the
list are simply enclosed in parentheses and separated by spaces. The
empty list is represented by ( ). For example,
(a b c d e)
and:
(a . (b . (c . (d . (e . ())))))
are equivalent notations for a list of symbols.
The general rule is that, if a dot is followed by an open parenthesis, the dot, open parenthesis, and matching closing parenthesis can be omitted in the external representation.
The sequence of characters (4 . 5) is the external representation
of a pair, not an expression that evaluates to a pair. Similarly, the
sequence of characters (+ 2 6) is not an external
representation of the integer 8, even though it is an expression
(in the language of the (rnrs base (6)) library) evaluating to the
integer 8; rather, it is a syntactic datum representing a three–element
list, the elements of which are the symbol + and the integers 2
and 6.
Vector data, representing vectors of objects, are represented using the
notation #(<datum> ...). For example, a vector of length 3
containing the number object for zero in element 0, the list (2 2
2 2) in element 1, and the string "Anna" in element 2 can be
represented as follows:
#(0 (2 2 2 2) "Anna")
This is the external representation of a vector, not an expression that evaluates to a vector.
Bytevector data, representing bytevectors (stdlib bytevector), are
represented using the notation #vu8(<u8> ...), where the
<u8>s represent the octets of the bytevector. For example, a
bytevector of length 3 containing the octets 2, 24, and 123 can be
represented as follows:
#vu8(2 24 123)
This is the external representation of a bytevector, and also an expression that evaluates to a bytevector.
'<datum> `<datum> ,<datum>
,@<datum> #'<datum> #`<datum>
#,<datum> #,@<datum>
Each of these is an abbreviation:
'<datum>(quote <datum>),
`<datum>(quasiquote <datum>),
,<datum>(unquote <datum>),
,@<datum>(unquote-splicing <datum>),
#'<datum>(syntax <datum>),
#`<datum>(quasisyntax <datum>),
#,<datum>(unsyntax <datum>), and
#,@<datum>(unsyntax-splicing <datum>).
A Scheme program consists of a top–level program together with a set of libraries, each of which defines a part of the program connected to the others through explicitly specified exports and imports.
Chapters “Libraries” and “Top–level programs” describe the syntax and semantics of libraries and top–level programs, respectively. Chapter “Base library” describes a base library that defines many of the constructs traditionally associated with Scheme. A separate report describes the various standard libraries provided by a Scheme system.
The division between the base library and the other standard libraries is based on use, not on construction. In particular, some facilities that are typically implemented as “primitives” by a compiler or the run–time system rather than in terms of other standard procedures or syntactic forms are not part of the base library, but are defined in separate libraries. Examples include the fixnums and flonums libraries, the exceptions and conditions libraries, and the libraries for records.
Within the body of a library or top–level program, an identifier may name a kind of syntax, or it may name a location where a value can be stored. An identifier that names a kind of syntax is called a keyword, and is said to be bound to that kind of syntax (or, in the case of a syntactic abstraction, a transformer that translates the syntax into more primitive forms). An identifier that names a location is called a variable and is said to be bound to that location. At each point within a top–level program or a library, a specific, fixed set of identifiers is bound. The set of these identifiers, the set of visible bindings, is known as the environment in effect at that point.
Certain forms are used to create syntactic abstractions and to bind
keywords to transformers for those new syntactic abstractions, while
other forms create new locations and bind variables to those locations.
Collectively, these forms are called binding constructs. Some
binding constructs take the form of definitions, while others are
expressions. With the exception of exported library bindings, a binding
created by a definition is visible only within the body in which the
definition appears, e.g. the body of a library, top–level program, or
lambda expression. Exported library bindings are also visible
within the bodies of the libraries and top–level programs that import
them.
Expressions that bind variables include the lambda, let,
let*, letrec, letrec*, let-values, and
let*-values forms from the base library. Of these, lambda
is the most fundamental. Variable definitions appearing within the body
of such an expression, or within the bodies of a library or top–level
program, are treated as a set of letrec* bindings. In addition,
for library bodies, the variables exported from the library can be
referenced by importing libraries and top–level programs.
Expressions that bind keywords include the let-syntax and
letrec-syntax forms. A define form is a definition that
creates a variable binding, and a define-syntax form is a
definition that creates a keyword binding.
Scheme is a statically scoped language with block structure. To each
place in a top–level program or library body where an identifier is
bound there corresponds a region of code within which the binding
is visible. The region is determined by the particular binding
construct that establishes the binding; if the binding is established by
a lambda expression, for example, then its region is the entire
lambda expression. Every mention of an identifier refers to the
binding of the identifier that establishes the innermost of the regions
containing the use. If a use of an identifier appears in a place where
none of the surrounding expressions contains a binding for the
identifier, the use may refer to a binding established by a definition
or import at the top of the enclosing library or top–level program. If
there is no binding for the identifier, it is said to be unbound.
A variety of exceptional situations are distinguished in this report, among them violations of syntax, violations of a procedure's specification, violations of implementation restrictions, and exceptional situations in the environment. When an exceptional situation is detected by the implementation, an exception is raised, which means that a special procedure called the current exception handler is called. A program can also raise an exception, and override the current exception handler; stdlib exceptions.
When an exception is raised, an object is provided that describes the nature of the exceptional situation. The report uses the condition system described in library section “Conditions” to describe exceptional situations, classifying them by condition types.
Some exceptional situations allow continuing the program if the exception handler takes appropriate action. The corresponding exceptions are called continuable. For most of the exceptional situations described in this report, portable programs cannot rely upon the exception being continuable at the place where the situation was detected. For those exceptions, the exception handler that is invoked by the exception should not return. In some cases, however, continuing is permissible, and the handler may return. stdlib exceptions
Implementations must raise an exception when they are unable to continue
correct execution of a correct program due to some implementation
restriction. For example, an implementation that does not support
infinities must raise an exception with condition type
&implementation-restriction when it evaluates an expression whose
result would be an infinity.
Some possible implementation restrictions such as the lack of representations for NaNs and infinities are anticipated by this report, and implementations typically must raise an exception of the appropriate condition type if they encounter such a situation.
This report uses the phrase “an exception is raised” synonymously with “an exception must be raised”. This report uses the phrase “an exception with condition type t” to indicate that the object provided with the exception is a condition object of the specified type. The phrase “a continuable exception is raised” indicates an exceptional situation that permits the exception handler to return.
Many procedures specified in this report or as part of a standard library restrict the arguments they accept. Typically, a procedure accepts only specific numbers and types of arguments. Many syntactic forms similarly restrict the values to which one or more of their subforms can evaluate. These restrictions imply responsibilities for both the programmer and the implementation. Specifically, the programmer is responsible for ensuring that the values indeed adhere to the restrictions described in the specification. The implementation must check that the restrictions in the specification are indeed met, to the extent that it is reasonable, possible, and necessary to allow the specified operation to complete successfully.
Note that it is not always possible for an implementation to completely
check the restrictions set forth in a specification. For example, if an
operation is specified to accept a procedure with specific properties,
checking of these properties is undecidable in general. Similarly, some
operations accept both lists and procedures that are called by these
operations. Since lists can be mutated by the procedures through the
(rnrs mutable-pairs (6)) library, an argument that is a list when the
operation starts may become a non–list during the execution of the
operation.
Also, the procedure might escape to a different continuation, preventing the operation from performing more checks. Requiring the operation to check that the argument is a list after each call to such a procedure would be impractical. Furthermore, some operations that accept lists only need to traverse these lists partially to perform their function; requiring the implementation to traverse the remainder of the list to verify that all specified restrictions have been met might violate reasonable performance assumptions. For these reasons, the programmer's obligations may exceed the checking obligations of the implementation.
When an implementation detects a violation of a restriction for an
argument, it must raise an exception with condition type
&assertion in a way consistent with the safety of execution as
described in section “Safety”.
The subforms of a special form usually need to obey certain syntactic restrictions. As forms may be subject to macro expansion, which may not terminate, the question of whether they obey the specified restrictions is undecidable in general.
When macro expansion terminates, however, implementations must detect violations of the syntax. A syntax violation is an error with respect to the syntax of library bodies, top–level bodies, or the “syntax” entries in the specification of the base library or the standard libraries. Moreover, attempting to assign to an immutable variable (i.e. the variables exported by a library) is also considered a syntax violation.
If a top–level or library form in a program is not syntactically
correct, then the implementation must raise an exception with condition
type &syntax, and execution of that top–level program or library
must not be allowed to begin.
The standard libraries whose exports are described by this document are said to be safe libraries. Libraries and top–level programs that import only from safe libraries are also said to be safe.
As defined by this document, the Scheme programming language is safe in the following sense: The execution of a safe top–level program cannot go so badly wrong as to crash or to continue to execute while behaving in ways that are inconsistent with the semantics described in this document, unless an exception is raised.
Violations of an implementation restriction must raise an exception with
condition type &implementation-restriction, as must all
violations and errors that would otherwise threaten system integrity in
ways that might result in execution that is inconsistent with the
semantics described in this document.
The above safety properties are guaranteed only for top–level programs and libraries that are said to be safe. In particular, implementations may provide access to unsafe libraries in ways that cannot guarantee safety.
Although there is a separate boolean type, any Scheme value can be used
as a boolean value for the purpose of a conditional test. In a
conditional test, all values count as true in such a test except for
#f. This report uses the word “true” to refer to any Scheme
value except #f, and the word “false” to refer to #f.
A Scheme expression can evaluate to an arbitrary finite number of values. These values are passed to the expression's continuation.
Not all continuations accept any number of values. For example, a
continuation that accepts the argument to a procedure call is guaranteed
to accept exactly one value. The effect of passing some other number of
values to such a continuation is unspecified. The
call-with-values procedure makes it possible to create
continuations that accept specified numbers of return values. If the
number of return values passed to a continuation created by a call to
call-with-values is not accepted by its consumer that was passed
in that call, then an exception is raised. A more complete description
of the number of values accepted by different continuations and the
consequences of passing an unexpected number of values is given in the
description of the values procedure.
A number of forms in the base library have sequences of expressions as subforms that are evaluated sequentially, with the return values of all but the last expression being discarded. The continuations discarding these values accept any number of values.
If an expression is said to “return unspecified values”, then the expression must evaluate without raising an exception, but the values returned depend on the implementation; this report explicitly does not say how many or what values should be returned. Programmers should not rely on a specific number of return values or the specific values themselves.
Variables and objects such as pairs, vectors, bytevectors, strings,
hashtables, and records implicitly refer to locations or sequences of
locations. A string, for example, contains as many locations as there
are characters in the string. (These locations need not correspond to a
full machine word.) A new value may be stored into one of these
locations using the string-set! procedure, but the string
contains the same locations as before.
An object fetched from a location, by a variable reference or by a
procedure such as car, vector-ref, or string-ref,
is equivalent in the sense of eqv? to the object last stored in
the location before the fetch.
Every location is marked to show whether it is in use. No variable or object ever refers to a location that is not in use. Whenever this report speaks of storage being allocated for a variable or object, what is meant is that an appropriate number of locations are chosen from the set of locations that are not in use, and the chosen locations are marked to indicate that they are now in use before the variable or object is made to refer to them.
It is desirable for constants (i.e. the values of literal expressions)
to reside in read-only memory. To express this, it is convenient to
imagine that every object that refers to locations is associated with a
flag telling whether that object is mutable. Literal constants, the
strings returned by symbol->string, records with no mutable
fields, and other values explicitly designated as immutable are
immutable objects, while all objects created by the other procedures
listed in this report are mutable. An attempt to store a new value into
a location referred to by an immutable object should raise an exception
with condition type &assertion.
Implementations of Scheme must be properly tail–recursive. Procedure calls that occur in certain syntactic contexts called tail contexts are tail calls.
A Scheme implementation is properly tail–recursive if it supports an
unbounded number of active tail calls. A call is active if the
called procedure may still return. Note that this includes regular
returns as well as returns through continuations captured earlier by
call-with-current-continuation that are later invoked. In the
absence of captured continuations, calls could return at most once and
the active calls would be those that had not yet returned. A formal
definition of proper tail recursion can be found in Clinger's paper
“Proper tail recursion and and space efficiency”. The rules for
identifying tail calls in constructs from the (rnrs base (6)) library
are described in section “Tail calls and tail contexts”.
For a procedure call, the time between when it is initiated and when it
returns is called its dynamic extent. In Scheme,
call-with-current-continuation allows reentering a dynamic extent
after its procedure call has returned. Thus, the dynamic extent of a
call may not be a single, connected time period.
Some operations described in the report acquire information in addition
to their explicit arguments from the dynamic environment. For
example, call-with-current-continuation accesses an implicit
context established by dynamic-wind, and the raise
procedure accesses the current exception handler.
The operations that modify the dynamic environment do so dynamically,
for the dynamic extent of a call to a procedure like dynamic-wind
or with-exception-handler. When such a call returns, the
previous dynamic environment is restored. The dynamic environment can
be thought of as part of the dynamic extent of a call. Consequently, it
is captured by call-with-current-continuation, and restored by
invoking the escape procedure it creates.
The chapters that describe bindings in the base library and the standard libraries are organized into entries. Each entry describes one language feature or a group of related features, where a feature is either a syntactic construct or a built–in procedure. An entry begins with one or more header lines of the form
The Category defines the kind of binding described by the entry, typically either “Syntax” or “Procedure”. An entry may specify various restrictions on subforms or arguments.
If category is “Syntax”, the entry describes a special
syntactic construct, and the template gives the syntax of the forms of
the construct. The template is written in a notation similar to a
right–hand side of the BNF rules in chapter “Lexical syntax
and datum syntax”, and describes the set of forms equivalent to the
forms matching the template as syntactic data. Some “Syntax” entries
carry a suffix (expand), specifying that the syntactic keyword of
the construct is exported with level 1. Otherwise, the syntactic
keyword is exported with level 0; lang library import export.
Components of the form described by a template are designated by syntactic variables, which are written using angle brackets, for example, <expression>, <variable>. Case is insignificant in syntactic variables. Syntactic variables stand for other forms, or sequences of them. A syntactic variable may refer to a non–terminal in the grammar for syntactic data, in which case only forms matching that non–terminal are permissible in that position. For example, <identifier> stands for a form which must be an identifier. Also, <expression> stands for any form which is a syntactically valid expression. Other non–terminals that are used in templates are defined as part of the specification.
The notation
<thing1> ...
indicates zero or more occurrences of a <thing>, and
<thing1> <thing2> ...
indicates one or more occurrences of a <thing>.
It is the programmer's responsibility to ensure that each component of a
form has the shape specified by a template. Descriptions of syntax may
express other restrictions on the components of a form. Typically, such
a restriction is formulated as a phrase of the form “<x> must be a
...”. Again, these specify the programmer's responsibility. It is
the implementation's responsibility to check that these restrictions are
satisfied, as long as the macro transformers involved in expanding the
form terminate. If the implementation detects that a component does not
meet the restriction, an exception with condition type &syntax
is raised.
If Category is “Procedure”, then the entry describes a procedure, and the header line gives a template for a call to the procedure. Parameter names in the template are shown like this: parm. Thus the header line:
indicates that the built–in procedure vector-ref takes two
arguments, a vector vector and an exact non–negative integer
object k (see below). The header lines:
indicate that the make-vector procedure takes either one or two
arguments. The parameter names are case–insensitive.
As with syntax templates, an ellipsis ... at the end of a header line, as in:
indicates that the procedure takes arbitrarily many arguments of the
same type as specified for the last parameter name. In this case,
= accepts two or more arguments that must all be complex number
objects.
A procedure that detects an argument that it is not specified to handle
must raise an exception with condition type &assertion. Also,
the argument specifications are exhaustive: if the number of arguments
provided in a procedure call does not match any number of arguments
accepted by the procedure, an exception with condition type
&assertion must be raised.
For succinctness, the report follows the convention that if a parameter
name is also the name of a type, then the corresponding argument must be
of the named type. For example, the header line for vector-ref
given above dictates that the first argument to vector-ref must
be a vector. The following naming conventions imply type restrictions:
#f or #t);
Other type restrictions are expressed through parameter–naming conventions that are described in specific chapters. For example, library chapter “Arithmetic” uses a number of special parameter variables for the various subsets of the numbers.
With the listed type restrictions, it is the programmer's responsibility to ensure that the corresponding argument is of the specified type. It is the implementation's responsibility to check for that type.
A parameter called list means that it is the programmer's responsibility to pass an argument that is a list. It is the implementation's responsibility to check that the argument is appropriately structured for the operation to perform its function, to the extent that this is possible and reasonable. The implementation must at least check that the argument is either an empty list or a pair.
Descriptions of procedures may express other restrictions on the arguments of a procedure. Typically, such a restriction is formulated as a phrase of the form “x must be a ...” (or otherwise using the word “must”).
In addition to the restrictions implied by naming conventions, an entry may list additional explicit restrictions. These explicit restrictions usually describe both the programmer's responsibilities, who must ensure that the subforms of a form are appropriate, or that an appropriate argument is passed, and the implementation's responsibilities, which must check that subform adheres to the specified restrictions (if macro expansion terminates), or if the argument is appropriate.
A description may explicitly list the implementation's responsibilities for some arguments or subforms in a paragraph labeled “Implementation responsibilities”. In this case, the responsibilities specified for these subforms or arguments in the rest of the description are only for the programmer. A paragraph describing implementation responsibility does not affect the implementation's responsibilities for checking subforms or arguments not mentioned in the paragraph.
If Category is something other than “Syntax” and “Procedure”, then the entry describes a non–procedural value, and the category describes the type of that value. The header line:
indicates that &who is a condition type. The header line:
indicates that unquote is a syntax binding that may occur only as
part of specific surrounding expressions. Any use as an independent
syntactic construct or identifier is a syntax violation. As with
“Syntax” entries, some “Auxiliary Syntax” entries carry a suffix
(expand), specifying that the syntactic keyword of the construct
is exported with level 1.
The description of an entry occasionally states that it is the same as another entry. This means that both entries are equivalent. Specifically, it means that if both entries have the same name and are thus exported from different libraries, the entries from both libraries can be imported under the same name without conflict.
The symbol ⇒ used in program examples can be read
“evaluates to”. For example:
(* 5 8)
⇒ 40
means that the expression (* 5 8) evaluates to the object
40. Or, more precisely: the expression given by the sequence of
characters (* 5 8) evaluates, in an environment that imports the
relevant library, to an object that may be represented externally by the
sequence of characters 40.
The ⇒ symbol is also used when the evaluation of an
expression causes a violation. For example:
(integer->char #xD800)
⇒ &assertion exception
means that the evaluation of the expression (integer->char
#xD800) must raise an exception with condition type &assertion.
Moreover, the ⇒ symbol is also used to explicitly say
that the value of an expression in unspecified. For example:
(eqv? "" "")
⇒ unspecified
Mostly, examples merely illustrate the behavior specified in the entry. In some cases, however, they disambiguate otherwise ambiguous specifications and are thus normative. Note that, in some cases, specifically in the case of inexact number objects, the return value is only specified conditionally or approximately. For example:
(atan -inf.0)
⇒ -1.5707963267948965 ; approximately
By convention, the names of procedures that store values into previously
allocated locations usually end in !.
By convention, -> appears within the names of procedures that
take an object of one type and return an analogous object of another
type. For example, list->vector takes a list and returns a
vector whose elements are the same as those of the list.
By convention, the names of predicates (procedures that always return a
boolean value) end in ? when the name contains any letters;
otherwise, the predicate's name does not end with a question mark.
By convention, the components of compound names are separated by
-. In particular, prefixes that are actual words or can be
pronounced as though they were actual words are followed by a hyphen,
except when the first character following the hyphen would be something
other than a letter, in which case the hyphen is omitted. Short,
unpronounceable prefixes (fx and fl) are not followed by a
hyphen.
By convention, the names of condition types start with &.
Libraries are parts of a program that can be distributed independently. The library system supports macro definitions within libraries, macro exports, and distinguishes the phases in which definitions and imports are needed. This chapter defines the notation for libraries and a semantics for library expansion and execution.
A library definition must have the following form:
(library <library name>
(export <export spec> ...)
(import <import spec> ...)
<library body>)
It is a syntax violation if a constraint given in this section is not met.
A library declaration contains the following elements:
export subform specifies a list of exports, which names a
subset of the bindings defined within or imported into the library.
import subform specifies the imported bindings as a list of
import dependencies, where each dependency specifies:
An identifier can be imported with the same local name from two or more libraries or for two levels from the same library only if the binding exported by each library is the same (i.e., the binding is defined in one library, and it arrives through the imports only by exporting and re–exporting). Otherwise, no identifier can be imported multiple times, defined multiple times, or both defined and imported. No identifiers are visible within a library except for those explicitly imported into the library or defined within the library.
A <library name> uniquely identifies a library within an
implementation, and is globally visible in the import clauses of
all other libraries within an implementation. A <library name> has
the following form:
(<identifier1> <identifier2> ... <version>)
where <version> is empty or has the following form:
(<sub-version> ...)
Each <sub-version> must represent an exact nonnegative integer
object. An empty <version> is equivalent to ().
An <export spec> names a set of imported and locally defined bindings to be exported, possibly with different external names. An <export spec> must have one of the following forms:
<identifier>
(rename (<identifier1> <identifier2>)
...)
In an <export spec>, an <identifier> names a single binding
defined within or imported into the library, where the external name for
the export is the same as the name of the binding within the library. A
rename spec exports the binding named by <identifier1> in
each (<identifier1> <identifier2>) pairing, using
<identifier2> as the external name.
Each <import spec> specifies a set of bindings to be imported into the library, the levels at which they are to be available, and the local names by which they are to be known. An <import spec> must be one of the following:
<import set>
(for <import set> <import level> ...)
An <import level> is one of the following:
run
expand
(meta <level>)
where <level> represents an exact integer object.
As an <import level>, run is an abbreviation for (meta
0), and expand is an abbreviation for (meta 1).
An <import set> names a set of bindings from another library and possibly specifies local names for the imported bindings. It must be one of the following:
<library reference>
(library <library reference>)
(only <import set> <identifier> ...)
(except <import set> <identifier> ...)
(prefix <import set> <identifier>)
(rename <import set> (<identifier1> <identifier2>) ...)
A <library reference> identifies a library by its name and optionally by its version. It has one of the following forms:
(<identifier1> <identifier2> ...)
(<identifier1> <identifier2> ... <version reference>)
A <library reference> whose first <identifier> is for,
library, only, except, prefix, or
rename is permitted only within a library <import
set>. The <import set> (library <library reference>)
is otherwise equivalent to <library reference>.
A <library reference> with no <version reference> (first form
above) is equivalent to a <library reference> with a <version
reference> of ().
A <version reference> specifies a set of <version>s that it matches. The <library reference> identifies all libraries of the same name and whose version is matched by the <version reference>. A <version reference> has the following form:
(<sub-version reference1> ... <sub-version referencen>)
(and <version reference> ...)
(or <version reference> ...)
(not <version reference>)
A <version reference> of the first form matches a <version>
with at least n elements, whose <sub-version reference>s
match the corresponding <sub-version>s. An and
<version reference> matches a version if all <version
references> following the and match it. Correspondingly, an
or <version reference> matches a version if one of
<version references> following the or matches it, and a
not <version reference> matches a version if the
<version reference> following it does not match it.
A <sub-version reference> has one of the following forms:
<sub-version>
(>= <sub-version>)
(<= <sub-version>)
(and <sub-version reference> ...)
(or <sub-version reference> ...)
(not <sub-version reference>)
A <sub-version reference> of the first form matches a
<sub-version> if it is equal to it. A >= <sub-version
reference> of the first form matches a sub–version if it is greater or
equal to the <sub-version> following it; analogously for <=.
An and <sub-version reference> matches a sub–version if all
of the subsequent <sub-version reference>s match it.
Correspondingly, an or <sub-version reference> matches a
sub–version if one of the subsequent <sub-version reference>s
matches it, and a not <sub-version reference> matches a
sub–version if the subsequent <sub-version reference> does not
match it.
Examples:
| version reference | version | match?
|
|---|---|---|
() | (1) | yes
|
(1) | (1) | yes
|
(1) | (2) | no
|
(2 3) | (2) | no
|
(2 3) | (2 3) | yes
|
(2 3) | (2 3 5) | yes
|
(or (1 (>= 1)) (2)) | (2) | yes
|
(or (1 (>= 1)) (2)) | (1 1) | yes
|
(or (1 (>= 1)) (2)) | (1 0) | no
|
((or 1 2 3)) | (1) | yes
|
((or 1 2 3)) | (2) | yes
|
((or 1 2 3)) | (3) | yes
|
((or 1 2 3)) | (4) | no
|
When more than one library is identified by a library reference, the choice of libraries is determined in some implementation–dependent manner.
To avoid problems such as incompatible types and replicated state, implementations should prohibit the two libraries whose library names consist of the same sequence of identifiers but whose versions do not match to co–exist in the same program.
By default, all of an imported library's exported bindings are made
visible within an importing library using the names given to the
bindings by the imported library. The precise set of bindings to be
imported and the names of those bindings can be adjusted with the
only, except, prefix, and rename forms as
described below.
onlyexceptprefixrename (rename (<identifier1> <identifier2>)
...)
removes the bindings for <identifier1> ... to form an
intermediate <import set>, then adds the bindings back for the
corresponding <identifier2> ... to form the final
<import set>. Each <identifier1> must be in the original
<import set>, each <identifier2> must not be in the
intermediate <import set>, and the <identifier2>s must be
distinct.
The <library body> of a library form consists of forms that
are classified as definitions or expressions. Which forms
belong to which class depends on the imported libraries and the result
of expansion. Generally, forms that are not definitions are
expressions.
A <library body> is like a <body> except that a <library body>s need not include any expressions. It must have the following form:
<definition> ... <expression> ...
When begin, let-syntax, or letrec-syntax forms
occur in a top–level body prior to the first expression, they are
spliced into the body. Some or all of the body, including portions
wrapped in begin, let-syntax, or letrec-syntax
forms, may be specified by a syntactic abstraction.
The transformer expressions and bindings are evaluated and created from
left to right, as described in Expansion process
The expressions of variable definitions are evaluated from left to
right, as if in an implicit letrec*, and the body expressions are
also evaluated from left to right after the expressions of the variable
definitions. A fresh location is created for each exported variable and
initialized to the value of its local counterpart. The effect of
returning twice to the continuation of the last body expression is
unspecified.
NOTE The nameslibrary,export,import,for,run,expand,meta,import,export,only,except,prefix,rename,and,or,not,>=, and<=appearing in the library syntax are part of the syntax and are not reserved, i.e., the same names can be used for other purposes within the library or even exported from or imported into a library with different meanings, without affecting their use in thelibraryform.
Bindings defined with a library are not visible in code outside of the library, unless the bindings are explicitly exported from the library. An exported macro may, however, implicitly export an otherwise unexported identifier defined within or imported into the library. That is, it may insert a reference to that identifier into the output code it produces.
All explicitly exported variables are immutable in both the exporting
and importing libraries. It is thus a syntax violation if an explicitly
exported variable appears on the left–hand side of a set!
expression, either in the exporting or importing libraries.
All implicitly exported variables are also immutable in both the
exporting and importing libraries. It is thus a syntax violation if a
variable appears on the left–hand side of a set! expression in
any code produced by an exported macro outside of the library in which
the variable is defined. It is also a syntax violation if a reference
to an assigned variable appears in any code produced by an exported
macro outside of the library in which the variable is defined, where an
assigned variable is one that appears on the left–hand side of a
set! expression in the exporting library.
All other variables defined within a library are mutable.
Expanding a library may require run–time information from another library. For example, if a macro transformer calls a procedure from library A, then the library A must be instantiated before expanding any use of the macro in library B. Library A may not be needed when library B is eventually run as part of a program, or it may be needed for run time of library B, too. The library mechanism distinguishes these times by phases, which are explained in this section.
Every library can be characterized by expand–time information (minimally, its imported libraries, a list of the exported keywords, a list of the exported variables, and code to evaluate the transformer expressions) and run–time information (minimally, code to evaluate the variable definition right–hand–side expressions, and code to evaluate the body expressions). The expand–time information must be available to expand references to any exported binding, and the run–time information must be available to evaluate references to any exported variable binding.
A phase is a time at which the expressions within a library are
evaluated. Within a library body, top–level expressions and the
right–hand sides of define forms are evaluated at run time,
i.e. phase 0, and the right–hand sides of define-syntax
forms are evaluated at expand time, i.e. phase 1. When
define-syntax, let-syntax, or letrec-syntax forms
appear within code evaluated at phase n, the right–hand sides
are evaluated at phase n+1.
These phases are relative to the phase in which the library itself is used. An instance of a library corresponds to an evaluation of its variable definitions and expressions in a particular phase relative to another library—a process called instantiation. For example, if a top–level expression in a library B refers to a variable export from another library A, then it refers to the export from an instance of A at phase 0 (relative to the phase of B). But if a phase 1 expression within B refers to the same binding from A, then it refers to the export from an instance of A at phase 1 (relative to the phase of B).
A visit of a library corresponds to the evaluation of its syntax definitions in a particular phase relative to another library; a process called visiting. For example, if a top–level expression in a library B refers to a macro export from another library A, then it refers to the export from a visit of A at phase 0 (relative to the phase of B), which corresponds to the evaluation of the macro's transformer expression at phase 1.
A level is a lexical property of an identifier that determines in
which phases it can be referenced. The level for each identifier bound
by a definition within a library is 0; that is, the identifier
can be referenced only at phase 0 within the library. The level
for each imported binding is determined by the enclosing for form
of the import in the importing library, in addition to the levels
of the identifier in the exporting library. Import and export levels
are combined by pairwise addition of all level combinations. For
example, references to an imported identifier exported for levels
p_a and p_b and imported for levels q_a,
q_b, and q_c are valid at levels p_a + q_a,
p_a + q_b, p_a + q_c, p_b + q_a, p_b + q_b,
and p_b + q_c. An <import set> without an enclosing
for is equivalent to (for <import set> run), which is
the same as (for <import set> (meta 0)).
The export level of an exported binding is 0 for all bindings that are defined within the exporting library. The export levels of a reexported binding, i.e. an export imported from another library, are the same as the effective import levels of that binding within the reexporting library.
For the libraries defined in the library report, the export level is
0 for nearly all bindings. The exceptions are
syntax-rules, identifier-syntax, ..., and _
from the (rnrs base (6)) library, which are exported with level
1, set! from the (rnrs base (6)) library, which is
exported with levels 0 and 1, and all bindings from the
composite (rnrs (6)) library, which are exported with levels
0 and 1.
Macro expansion within a library can introduce a reference to an identifier that is not explicitly imported into the library. In that case, the phase of the reference must match the identifier's level as shifted by the difference between the phase of the source library (i.e., the library that supplied the identifier's lexical context) and the library that encloses the reference. For example, suppose that expanding a library invokes a macro transformer, and the evaluation of the macro transformer refers to an identifier that is exported from another library (so the phase 1 instance of the library is used); suppose further that the value of the binding is a syntax object representing an identifier with only a level n binding; then, the identifier must be used only at phase n+1 in the library being expanded. This combination of levels and phases is why negative levels on identifiers can be useful, even though libraries exist only at non–negative phases.
If any of a library's definitions are referenced at phase 0 in the expanded form of a program, then an instance of the referenced library is created for phase 0 before the program's definitions and expressions are evaluated. This rule applies transitively: if the expanded form of one library references at phase 0 an identifier from another library, then before the referencing library is instantiated at phase n, the referenced library must be instantiated at phase n. When an identifier is referenced at any phase n greater than 0, in contrast, then the defining library is instantiated at phase n at some unspecified time before the reference is evaluated. Similarly, when a macro keyword is referenced at phase n during the expansion of a library, then the defining library is visited at phase n at some unspecified time before the reference is evaluated.
An implementation may distinguish instances/visits of a library for
different phases or to use an instance/visit at any phase as an
instance/visit at any other phase. An implementation may further expand
each library form with distinct visits of libraries in any phase
and/or instances of libraries in phases above 0. An
implementation may create instances/visits of more libraries at more
phases than required to satisfy references. When an identifier appears
as an expression in a phase that is inconsistent with the identifier's
level, then an implementation may raise an exception either at expand
time or run time, or it may allow the reference. Thus, a library whose
meaning depends on whether the instances of a library are distinguished
or shared across phases or library expansions may be unportable.
Examples for various <import spec>s and <export spec>s:
(library (stack)
(export make push! pop! empty!)
(import (rnrs)
(rnrs mutable-pairs))
(define (make)
(list '()))
(define (push! s v)
(set-car! s (cons v (car s))))
(define (pop! s)
(let ([v (caar s)])
(set-car! s (cdar s))
v))
(define (empty! s)
(set-car! s '())))
(library (balloons)
(export make push pop)
(import (rnrs))
(define (make w h)
(cons w h))
(define (push b amt)
(cons (- (car b) amt)
(+ (cdr b) amt)))
(define (pop b)
(display "Boom! ")
(display (* (car b) (cdr b)))
(newline)))
(library (party)
;; Total exports:
;; make, push, push!, make-party, pop!
(export (rename (balloon:make make)
(balloon:push push))
push!
make-party
(rename (party-pop! pop!)))
(import (rnrs)
(only (stack) make push! pop!) ; not empty!
(prefix (balloons) balloon:))
;; Creates a party as a stack of balloons,
;; starting with two balloons
(define (make-party)
(let ([s (make)]) ; from stack
(push! s (balloon:make 10 10))
(push! s (balloon:make 12 9))
s))
(define (party-pop! p)
(balloon:pop (pop! p))))
(library (main)
(export)
(import (rnrs) (party))
(define p (make-party))
(pop! p) ; displays "Boom! 108"
(push! p (push (make 5 5) 1))
(pop! p)) ; displays "Boom! 24"
Examples for macros and phases:
(library (my-helpers id-stuff)
(export find-dup)
(import (rnrs))
(define (find-dup l)
(and (pair? l)
(let loop ((rest (cdr l)))
(cond
[(null? rest) (find-dup (cdr l))]
[(bound-identifier=? (car l) (car rest))
(car rest)]
[else (loop (cdr rest))])))))
(library (my-helpers values-stuff)
(export mvlet)
(import (rnrs) (for (my-helpers id-stuff) expand))
(define-syntax mvlet
(lambda (stx)
(syntax-case stx ()
[(_ [(id ...) expr] body0 body ...)
(not (find-dup (syntax (id ...))))
(syntax
(call-with-values
(lambda () expr)
(lambda (id ...) body0 body ...)))]))))
(library (let-div)
(export let-div)
(import (rnrs)
(my-helpers values-stuff)
(rnrs r5rs))
(define (quotient+remainder n d)
(let ([q (quotient n d)])
(values q (- n (* q d)))))
(define-syntax let-div
(syntax-rules ()
[(_ n d (q r) body0 body ...)
(mvlet [(q r) (quotient+remainder n d)]
body0 body ...)])))
A “top–level program” specifies an entry point for defining and running a Scheme program. A top–level program specifies a set of libraries to import and code to run. Through the imported libraries, whether directly or through the transitive closure of importing, a top–level program defines a complete Scheme program.
A top–level program is a delimited piece of text, typically a file, that has the following form:
<import form> <top-level body>
An <import form> has the following form:
(import <import spec> ...)
A <top-level body> has the following form:
<top-level body form> ...
A <top-level body form> is either a <definition> or an <expression>.
The <import form> is identical to the import clause in libraries, and specifies a set of libraries to import. A <top-level body> is like a <library body>, except that definitions and expressions may occur in any order. Thus, the syntax specified by <top-level body form> refers to the result of macro expansion.
When uses of begin, let-syntax, or letrec-syntax
from the (rnrs base (6)) library occur in a top–level body prior to
the first expression, they are spliced into the body. Some or all of
the body, including portions wrapped in begin, let-syntax,
or letrec-syntax forms, may be specified by a syntactic
abstraction.
A top–level program is executed by treating the program similarly to a library, and evaluating its definitions and expressions. The semantics of a top–level body may be roughly explained by a simple translation into a library body: Each <expression> that appears before a definition in the top-level body is converted into a dummy definition
(define <variable>
(begin <expression> <unspecified>))
where <variable> is a fresh identifier and <unspecified> is a side–effect–free expression returning an unspecified value. (It is generally impossible to determine which forms are definitions and expressions without concurrently expanding the body, so the actual translation is somewhat more complicated.)
On platforms that support it, a top–level program may access its
command line by calling the command-line procedure, Command line access and exit values
After the import form within a library form or a
top–level program, the forms that constitute the body of the library or
the top–level program depend on the libraries that are imported. In
particular, imported syntactic keywords determine the available
syntactic abstractions and whether each form is a definition or
expression. A few form types are always available independent of
imported libraries, however, including constant literals, variable
references, procedure calls, and macro uses.
The entries in this section all describe expressions, which may occur in the place of <expression> syntactic variables.
An expression consisting of a representation of a number object, a boolean, a character, a string, or a bytevector, evaluates “to itself”.
145932 ⇒ 145932 #t ⇒ #t "abc" ⇒ "abc" #vu8(2 24 123) ⇒ #vu8(2 24 123)The value of a literal expression is immutable, Storage model
An expression consisting of a variable is a variable reference if it is not a macro use (see below). The value of the variable reference is the value stored in the location to which the variable is bound. It is a syntax violation to reference an unbound variable.
The following example examples assumes the base library has been imported:
(define x 28) x ⇒ 28
A procedure call consists of expressions for the procedure to be called and the arguments to be passed to it, with enclosing parentheses. A form in an expression context is a procedure call if <operator> is not an identifier bound as a syntactic keyword.
When a procedure call is evaluated, the operator and operand expressions are evaluated (in an unspecified order) and the resulting procedure is passed the resulting arguments.
The following examples assume the
(rnrs base (6))library has been imported:(+ 3 4) ⇒ 7 ((if #f + *) 3 4) ⇒ 12If the value of <operator> is not a procedure, an exception with condition type
&assertionis raised. Also, if <operator> does not accept as many arguments as there are <operand>s, an exception with condition type&assertionis raised.NOTE In contrast to other dialects of Lisp, the order of evaluation is unspecified, and the operator expression and the operand expressions are always evaluated with the same evaluation rules.Although the order of evaluation is otherwise unspecified, the effect of any concurrent evaluation of the operator and operand expressions is constrained to be consistent with some sequential order of evaluation. The order of evaluation may be chosen differently for each procedure call.
NOTE In many dialects of Lisp, the form()is a legitimate expression. In Scheme, expressions written as list/pair forms must have at least one subexpression, so()is not a syntactically valid expression.
Libraries and top–level programs can define and use new kinds of derived expressions and definitions called syntactic abstractions or macros. A syntactic abstraction is created by binding a keyword to a macro transformer or, simply, transformer.
The transformer determines how a use of the macro (called a macro use) is transcribed into a more primitive form.
Most macro uses have the form:
(<keyword> <datum> ...)
where <keyword> is an identifier that uniquely determines the kind of form. This identifier is called the syntactic keyword, or simply keyword. The number of <datum>s and the syntax of each depends on the syntactic abstraction.
Macro uses can also take the form of improper lists, singleton
identifiers, or set! forms, where the second subform of the
set! is the keyword:
(<keyword> <datum> ... . <datum>)
<keyword>
(set! <keyword> <datum>)
The define-syntax, let-syntax and letrec-syntax
forms create bindings for keywords, associate them with macro
transformers, and control the scope within which they are visible.
The syntax-rules and identifier-syntax forms create
transformers via a pattern language. Moreover, the syntax-case
form allows creating transformers via arbitrary Scheme code.
Keywords occupy the same name space as variables. That is, within the same scope, an identifier can be bound as a variable or keyword, or neither, but not both, and local bindings of either kind may shadow other bindings of either kind.
Macros defined using syntax-rules and identifier-syntax
are “hygienic” and “referentially transparent” and thus preserve
Scheme's lexical scoping.
Macros defined using the syntax-case facility are also hygienic
unless datum->syntax is used.
Macro uses are expanded into core forms at the start of evaluation (before compilation or interpretation) by a syntax expander. The set of core forms is implementation–dependent, as is the representation of these forms in the expander's output.
Information about identifier bindings is maintained during expansion to enforce lexical scoping for variables and keywords.
To handle definitions, the expander processes the initial forms in a <body> or <library body> from left to right. How the expander processes each form encountered depends upon the kind of form.
define-syntax formdefine formbegin formlet-syntax or letrec-syntax formlet-syntax and letrec-syntax to be visible only in the
inner body forms.
letrec* form from the defined
variables, expanded right–hand–side expressions, and expanded body
expressions.
For the right–hand side of the definition of a variable, expansion is deferred until after all of the definitions have been seen. Consequently, each keyword and variable reference within the right–hand side resolves to the local binding, if any.
A definition in the sequence of forms must not define any identifier whose binding is used to determine the meaning of the undeferred portions of the definition or any definition that precedes it in the sequence of forms. For example, the bodies of the following expressions violate this restriction.
(let ()
(define define 17)
(list define))
(let-syntax ((def0 (syntax-rules ()
((_ x) (define x 0)))))
(let ((z 3))
(def0 z)
(define def0 list)
(list z)))
(let ()
(define-syntax foo
(lambda (e)
(+ 1 2)))
(define + 2)
(foo))
The following do not violate the restriction.
(let ((x 5))
(define lambda list)
(lambda x x)) ⇒ (5 5)
(let-syntax ((def0 (syntax-rules ()
((_ x) (define x 0)))))
(let ((z 3))
(define def0 list)
(def0 z)
(list z))) ⇒ (3)
(let ()
(define-syntax foo
(lambda (e)
(let ((+ -)) (+ 1 2))))
(define + 2)
(foo)) ⇒ -1
The implementation should treat a violation of the restriction as a syntax violation.
Note that this algorithm does not directly reprocess any form. It requires a single left–to–right pass over the definitions followed by a single pass (in any order) over the body expressions and deferred right–hand sides.
Example:
(lambda (x)
(define-syntax defun
(syntax-rules ()
((_ x a e) (define x (lambda a e)))))
(defun even? (n) (or (= n 0) (odd? (- n 1))))
(define-syntax odd?
(syntax-rules () ((_ n) (not (even? n)))))
(odd? (if (odd? x) (* x x) x)))
In the example:
defun is encountered first, and the keyword
defun is associated with the transformer resulting from the
expansion and evaluation of the corresponding right–hand side.
defun is encountered next and expands into a
define form. Expansion of the right–hand side of this
define form is deferred.
odd? is next and results in the association of
the keyword odd? with the transformer resulting from expanding
and evaluating the corresponding right–hand side.
odd? appears next and is expanded; the resulting call
to not is recognized as an expression because not is bound
as a variable.
not) and the deferred right–hand side of
the even? definition; the uses of odd? appearing in these
expressions are expanded using the transformer associated with the
keyword odd?.
(lambda (x)
(letrec* ((even? (lambda (n)
(or (= n 0)
(not (even? (- n 1)))))))
(not (even? (if (not (even? x))
(* x x)
x)))))
although the structure of the output is implementation-dependent.
Because definitions and expressions can be interleaved in a <top-level body>, the expander's processing of a <top-level body> is somewhat more complicated. It behaves as described above for a <body> or <library body> with the following exceptions:
letrec* form from the defined variables, expanded
right–hand–side expressions, and expanded body expressions.
letrec* bindings, with a
fresh temporary variable on the left–hand side and the equivalent of:
(begin <expression> <unspecified>)
where <unspecified> is a side–effect–free expression returning an
unspecified value, on the right–hand side, so that left–to–right
evaluation order is preserved. The begin wrapper allows
<expression> to evaluate to an arbitrary number of values.
This appendix contains sample definitions for some of the keywords described in this report in terms of simpler forms:
condThe cond keyword could be defined in terms of if,
let and begin using syntax-rules as follows:
(define-syntax cond
(syntax-rules (else =>)
((cond (else result1 result2 ...))
(begin result1 result2 ...))
((cond (test => result))
(let ((temp test))
(if temp (result temp))))
((cond (test => result) clause1 clause2 ...)
(let ((temp test))
(if temp
(result temp)
(cond clause1 clause2 ...))))
((cond (test)) test)
((cond (test) clause1 clause2 ...)
(let ((temp test))
(if temp
temp
(cond clause1 clause2 ...))))
((cond (test result1 result2 ...))
(if test (begin result1 result2 ...)))
((cond (test result1 result2 ...)
clause1 clause2 ...)
(if test
(begin result1 result2 ...)
(cond clause1 clause2 ...)))))
caseThe case keyword could be defined in terms of let,
cond, and memv using syntax-rules as follows:
(define-syntax case
(syntax-rules (else)
((case expr0
((key ...) res1 res2 ...)
...
(else else-res1 else-res2 ...))
(let ((tmp expr0))
(cond
((memv tmp '(key ...)) res1 res2 ...)
...
(else else-res1 else-res2 ...))))
((case expr0
((keya ...) res1a res2a ...)
((keyb ...) res1b res2b ...)
...)
(let ((tmp expr0))
(cond
((memv tmp '(keya ...)) res1a res2a ...)
((memv tmp '(keyb ...)) res1b res2b ...)
...)))))
let*The let* keyword could be defined in terms of let using
syntax-rules as follows:
(define-syntax let*
(syntax-rules ()
((let* () body1 body2 ...)
(let () body1 body2 ...))
((let* ((name1 expr1) (name2 expr2) ...)
body1 body2 ...)
(let ((name1 expr1))
(let* ((name2 expr2) ...)
body1 body2 ...)))))
letrecThe letrec keyword could be defined approximately in terms of
let and set! using syntax-rules, using a helper to
generate the temporary variables needed to hold the values before the
assignments are made, as follows:
(define-syntax letrec
(syntax-rules ()
((letrec () body1 body2 ...)
(let () body1 body2 ...))
((letrec ((var init) ...) body1 body2 ...)
(letrec-helper
(var ...)
()
((var init) ...)
body1 body2 ...))))
(define-syntax letrec-helper
(syntax-rules ()
((letrec-helper
()
(temp ...)
((var init) ...)
body1 body2 ...)
(let ((var <undefined>) ...)
(let ((temp init) ...)
(set! var temp)
...)
(let () body1 body2 ...)))
((letrec-helper
(x y ...)
(temp ...)
((var init) ...)
body1 body2 ...)
(letrec-helper
(y ...)
(newtemp temp ...)
((var init) ...)
body1 body2 ...))))
The syntax <undefined> represents an expression that returns
something that, when stored in a location, causes an exception with
condition type &assertion to be raised if an attempt to read from
or write to the location occurs before the assignments generated by the
letrec transformation take place. (No such expression is defined
in Scheme.)
letrec*The letrec* keyword could be defined approximately in terms of
let and set! using syntax-rules as follows:
(define-syntax letrec*
(syntax-rules ()
((letrec* ((var1 init1) ...) body1 body2 ...)
(let ((var1 <undefined>) ...)
(set! var1 init1)
...
(let () body1 body2 ...)))))
The syntax <undefined> is as in the definition of letrec
above.
let-valuesThe following definition of let-values using syntax-rules
employs a pair of helpers to create temporary names for the formals.
(define-syntax let-values
(syntax-rules ()
((let-values (binding ...) body1 body2 ...)
(let-values-helper1
()
(binding ...)
body1 body2 ...))))
(define-syntax let-values-helper1
;; map over the bindings
(syntax-rules ()
((let-values
((id temp) ...)
()
body1 body2 ...)
(let ((id temp) ...) body1 body2 ...))
((let-values
assocs
((formals1 expr1) (formals2 expr2) ...)
body1 body2 ...)
(let-values-helper2
formals1
()
expr1
assocs
((formals2 expr2) ...)
body1 body2 ...))))
(define-syntax let-values-helper2
;; create temporaries for the formals
(syntax-rules ()
((let-values-helper2
()
temp-formals
expr1
assocs
bindings
body1 body2 ...)
(call-with-values
(lambda () expr1)
(lambda temp-formals
(let-values-helper1
assocs
bindings
body1 body2 ...))))
((let-values-helper2
(first . rest)
(temp ...)
expr1
(assoc ...)
bindings
body1 body2 ...)
(let-values-helper2
rest
(temp ... newtemp)
expr1
(assoc ... (first newtemp))
bindings
body1 body2 ...))
((let-values-helper2
rest-formal
(temp ...)
expr1
(assoc ...)
bindings
body1 body2 ...)
(call-with-values
(lambda () expr1)
(lambda (temp ... . newtemp)
(let-values-helper1
(assoc ... (rest-formal newtemp))
bindings
body1 body2 ...))))))
let*-valuesThe following macro defines let*-values in terms of let
and let-values using syntax-rules:
(define-syntax let*-values
(syntax-rules ()
((let*-values () body1 body2 ...)
(let () body1 body2 ...))
((let*-values (binding1 binding2 ...)
body1 body2 ...)
(let-values (binding1)
(let*-values (binding2 ...)
body1 body2 ...)))))
letThe let keyword could be defined in terms of lambda and
letrec using syntax-rules as follows:
(define-syntax let
(syntax-rules ()
((let ((name val) ...) body1 body2 ...)
((lambda (name ...) body1 body2 ...)
val ...))
((let tag ((name val) ...) body1 body2 ...)
((letrec ((tag (lambda (name ...)
body1 body2 ...)))
tag)
val ...))))
This chapter describes Scheme's (rnrs base (6)) library, which
exports many of the procedure and syntax bindings that are traditionally
associated with Scheme.
No object satisfies more than one of the following predicates:
boolean? pair?
symbol? number?
char? string?
vector? procedure?
null?
These predicates define the base types boolean, pair, symbol, number, char (or character), string, vector, and procedure. Moreover, the empty list is a special object of its own type.
Note that, although there is a separate boolean type, any Scheme value can be used as a boolean value for the purpose of a conditional test.
Definitions may appear within a <top-level body>, at the top of a <library body>, or at the top of a <body>.
A <definition> may be a variable definition or keyword definition.
Macro uses that expand into definitions or groups of definitions
(packaged in a begin, let-syntax, or letrec-syntax
form) may also appear wherever other definitions may appear.
The define form described in this section is a <definition>
used to create variable bindings and may appear anywhere other
definitions may appear.
The first from of
definebinds <variable> to a new location before assigning the value of <expression> to it.(define add3 (lambda (x) (+ x 3))) (add3 3) ⇒ 6 (define first car) (first '(1 2)) ⇒ 1The continuation of <expression> should not be invoked more than once.
Implementation responsibilities: Implementations should detect that the continuation of <expression> is invoked more than once. If the implementation detects this, it must raise an exception with condition type
&assertion.The second form of
defineis equivalent to:(define <variable> <unspecified>)where <unspecified> is a side–effect–free expression returning an unspecified value.
In the third form of
define, <formals> must be either a sequence of zero or more variables, or a sequence of one or more variables followed by a dot.and another variable (as in a lambda expression). This form is equivalent to:(define <variable> (lambda (<formals>) <body>))In the fourth form of
define, <formal> must be a single variable. This form is equivalent to:(define <variable> (lambda <formal> <body>))
The define-syntax form described in this section is a
<definition> used to create keyword bindings and may appear
anywhere other definitions may appear.
Binds <keyword> to the value of <expression>, which must evaluate, at macro–expansion time, to a transformer. Macro transformers can be created using the
syntax-rulesandidentifier-syntaxforms.Keyword bindings established by
define-syntaxare visible throughout the body in which they appear, except where shadowed by other bindings, and nowhere else, just like variable bindings established bydefine. All bindings established by a set of definitions, whether keyword or variable definitions, are visible within the definitions themselves.Implementation responsibilities: The implementation should detect if the value of <expression> cannot possibly be a transformer.
Example:
(let () (define even? (lambda (x) (or (= x 0) (odd? (- x 1))))) (define-syntax odd? (syntax-rules () ((odd? x) (not (even? x))))) (even? 10)) ⇒ #tAn implication of the left–to–right processing order is that one definition can affect whether a subsequent form is also a definition.
Example:
(let () (define-syntax bind-to-zero (syntax-rules () ((bind-to-zero id) (define id 0)))) (bind-to-zero x) x) ⇒ 0The behavior is unaffected by any binding for
bind-to-zerothat might appear outside of theletexpression.
The <body> of a lambda, let, let*,
let-values, let*-values, letrec, or letrec*
expression, or that of a definition with a body consists of zero or more
definitions followed by one or more expressions:
<definition> ... <expression1> <expression2> ...
Each identifier defined by a <definition> is local to the <body>; that is, the identifier is bound, and the region of the binding is the entire <body>.
Example:
(let ((x 5))
(define foo (lambda (y) (bar x y)))
(define bar (lambda (a b) (+ (* a b) a)))
(foo (+ x 3)))
⇒ 45
When begin, let-syntax, or letrec-syntax forms
occur in a body prior to the first expression, they are spliced into the
body. Some or all of the body, including portions wrapped in
begin, let-syntax, or letrec-syntax forms, may be
specified by a macro use.
An expanded <body> containing variable definitions can always be
converted into an equivalent letrec* expression. For example,
the let expression in the above example is equivalent to
(let ((x 5))
(letrec* ((foo (lambda (y) (bar x y)))
(bar (lambda (a b) (+ (* a b) a))))
(foo (+ x 3))))
The entries in this section describe the expressions of the
(rnrs base (6)) library, which may occur in the position of the
<expression> syntactic variable in addition to the primitive
expression types.
<Datum> should be a syntactic datum.
(quote <datum>)evaluates to the datum value represented by <datum>. This notation is used to include constants.(quote a) ⇒ a (quote #(a b c)) ⇒ #(a b c) (quote (+ 1 2)) ⇒ (+ 1 2)
(quote <datum>)may be abbreviated as'<datum>:'"abc" ⇒ "abc" '145932 ⇒ 145932 'a ⇒ a '#(a b c) ⇒ #(a b c) '() ⇒ () '(+ 1 2) ⇒ (+ 1 2) '(quote a) ⇒ (quote a) ''a ⇒ (quote a)Constants are immutable.
NOTE Different constants that are the value of aquoteexpression may share the same locations.
<formals> must be a formal parameter list as described below, and <body> must be as described in baselib bodies.
A
lambdaevaluates to a procedure. The environment in effect when thelambdais evaluated is remembered as part of the procedure. When the procedure is later called with some arguments, the environment in which thelambdawas evaluated is extended by binding the variables in the parameter list to fresh locations, and the resulting argument values are stored in those locations. Then, the expressions in the body of thelambda(which may contain definitions and thus represent aletrec*form) are evaluated sequentially in the extended environment. The results of the last expression in the body are returned as the results of the procedure call.(lambda (x) (+ x x)) ⇒ a procedure ((lambda (x) (+ x x)) 4) ⇒ 8 ((lambda (x) (define (p y) (+ y 1)) (+ (p x) x)) 5) ⇒ 11 (define reverse-subtract (lambda (x y) (- y x))) (reverse-subtract 7 10) ⇒ 3 (define add4 (let ((x 4)) (lambda (y) (+ x y)))) (add4 6) ⇒ 10<formals> must have one of the following forms:
(<variable1> ...)- The procedure takes a fixed number of arguments; when the procedure is called, the arguments are stored in the bindings of the corresponding variables.
<variable>- The procedure takes any number of arguments; when the procedure is called, the sequence of arguments is converted into a newly allocated list, and the list is stored in the binding of the <variable>.
(<variable1> ... <variableN> . <variableN+1>)- If a period
.precedes the last variable, then the procedure takes n or more arguments, where n is the number of parameters before the period (there must be at least one). The value stored in the binding of the last variable is a newly allocated list of the arguments left over after all the other arguments have been matched up against the other parameters.((lambda x x) 3 4 5 6) ⇒ (3 4 5 6) ((lambda (x y . z) z) 3 4 5 6) ⇒ (5 6)Any <variable> must not appear more than once in <formals>.
<test>, <consequent>, and <alternate> must be expressions.
An
ifexpression is evaluated as follows: first, <test> is evaluated. If it yields a true value, then <consequent> is evaluated and its values are returned. Otherwise <alternate> is evaluated and its values are returned. If <test> yields#fand no <alternate> is specified, then the result of the expression is unspecified.(if (> 3 2) 'yes 'no) ⇒ yes (if (> 2 3) 'yes 'no) ⇒ no (if (> 3 2) (- 3 2) (+ 3 2)) ⇒ 1 (if #f #f) ⇒ unspecifiedThe <consequent> and <alternate> expressions are in tail context if the
ifexpression itself is.
<expression> is evaluated, and the resulting value is stored in the location to which <variable> is bound. <variable> must be bound either in some region expression or at the top level. The result of the
set!expression is unspecified.(let ((x 2)) (+ x 1) (set! x 4) (+ x 1)) ⇒ 5It is a syntax violation if <variable> refers to an immutable binding.
NOTE The identifierset!is exported with level 1 as well.
Each <cond clause> must be of the form:
(<test> <expression1> ...)where <test> is an expression. Alternatively, a <cond clause> may be of the form:
(<test> => <expression>)The last <cond clause> may be an “
elseclause”, which has the form:(else <expression1> <expression2> ...)A
condexpression is evaluated by evaluating the <test> expressions of successive <cond clause>s in order until one of them evaluates to a true value. When a <test> evaluates to a true value, then the remaining <expression>s in its <cond clause> are evaluated in order, and the results of the last <expression> in the <cond clause> are returned as the results of the entirecondexpression.If the selected <cond clause> contains only the <test> and no <expression>s, then the value of the <test> is returned as the result. If the selected <cond clause> uses the ‘=>’ alternate form, then the <expression> is evaluated. Its value must be a procedure. This procedure should accept one argument; it is called on the value of the <test> and the values returned by this procedure are returned by the
condexpression.If all <test>s evaluate to
#f, and there is no ‘else’ clause, then the conditional expression returns unspecified values; if there is anelseclause, then its <expression>s are evaluated, and the values of the last one are returned.(cond ((> 3 2) 'greater) ((< 3 2) 'less)) ⇒ greater (cond ((> 3 3) 'greater) ((< 3 3) 'less) (else 'equal)) ⇒ equal (cond ('(1 2 3) => cadr) (else #f)) ⇒ 2For a <cond clause> of one of the following forms:
(<test> <expression1> ...) (else <expression1> <expression2> ...)the last <expression> is in tail context if the
condform itself is. For a <cond clause> of the form:(<test> => <expression>)the (implied) call to the procedure that results from the evaluation of <expression> is in a tail context if the
condform itself is.
<key> must be an expression. Each <case clause> must have one of the following forms:
((<datum1> ...) <expression1> <expression2> ...) (else <expression1> <expression2> ...)The second form, which specifies an “
elseclause”, may only appear as the last <case clause>. Each <datum> is an external representation of some object. The data represented by the <datum>s need not be distinct.A
caseexpression is evaluated as follows.
- <key> is evaluated and its result is compared using
eqv?against the data represented by the <datum>s of each <case clause> in turn, proceeding in order from left to right through the set of clauses.- If the result of evaluating <key> is equivalent to a datum of a <case clause>, the corresponding <expression>s are evaluated from left to right and the results of the last expression in the <case clause> are returned as the results of the
caseexpression. Otherwise, the comparison process continues.- If the result of evaluating <key> is different from every datum in each set, then if there is an
elseclause its expressions are evaluated and the results of the last are the results of thecaseexpression; otherwise thecaseexpression returns unspecified values.(case (* 2 3) ((2 3 5 7) 'prime) ((1 4 6 8 9) 'composite)) ⇒ composite (case (car '(c d)) ((a) 'a) ((b) 'b)) ⇒ unspecified (case (car '(c d)) ((a e i o u) 'vowel) ((w y) 'semivowel) (else 'consonant)) ⇒ consonantThe last <expression> of a <case clause> is in tail context if the
caseexpression itself is.
The <test>s must be expressions.
If there are no <test>s,
#tis returned. Otherwise, the <test> expressions are evaluated from left to right until a <test> returns#for the last <test> is reached. In the former case, theandexpression returns#fwithout evaluating the remaining expressions. In the latter case, the last expression is evaluated and its values are returned.(and (= 2 2) (> 2 1)) ⇒ #t (and (= 2 2) (< 2 1)) ⇒ #f (and 1 2 'c '(f g)) ⇒ (f g) (and) ⇒ #tThe
andkeyword could be defined in terms ofifusingsyntax-rulesas follows:(define-syntax and (syntax-rules () ((and) #t) ((and test) test) ((and test1 test2 ...) (if test1 (and test2 ...) #t))))The last <test> expression is in tail context if the
andexpression itself is.
The <test>s must be expressions.
If there are no <test>s,
#fis returned. Otherwise, the <test> expressions are evaluated from left to right until a <test> returns a true value val or the last <test> is reached. In the former case, theorexpression returns val without evaluating the remaining expressions. In the latter case, the last expression is evaluated and its values are returned.(or (= 2 2) (> 2 1)) ⇒ #t (or (= 2 2) (< 2 1)) ⇒ #t (or #f #f #f) ⇒ #f (or '(b c) (/ 3 0)) ⇒ (b c)The
orkeyword could be defined in terms ofifusingsyntax-rulesas follows:(define-syntax or (syntax-rules () ((or) #f) ((or test) test) ((or test1 test2 ...) (let ((x test1)) (if x x (or test2 ...))))))The last <test> expression is in tail context if the
orexpression itself is.
The binding constructs described in this section create local bindings
for variables that are visible only in a delimited region. The syntax
of the constructs let, let*, letrec, and
letrec* is identical, but they differ in the regions they
establish for their variable bindings and in the order in which the
values for the bindings are computed.
let expression, the initial values are computed before any
of the variables become bound; in a let* expression, the bindings
and evaluations are performed sequentially.
letrec or letrec* expression, all the bindings are in
effect while their initial values are being computed, thus allowing
mutually recursive definitions.
letrec expression, the initial values are computed before
being assigned to the variables; in a letrec*, the evaluations
and assignments are performed sequentially.
In addition, the binding constructs let-values and
let*-values generalize let and let* to allow
multiple variables to be bound to the results of expressions that
evaluate to multiple values. They are analogous to let and
let* in the way they establish regions: in a let-values
expression, the initial values are computed before any of the variables
become bound; in a let*-values expression, the bindings are
performed sequentially.
<bindings> must have the form:
((<variable1> <init1>) ...)where each <init> is an expression, and <body> is as described in baselib bodies. Any variable must not appear more than once in the <variable>s.
The <init>s are evaluated in the current environment (in some unspecified order), the <variable>s are bound to fresh locations holding the results, the <body> is evaluated in the extended environment, and the values of the last expression of <body> are returned. Each binding of a <variable> has <body> as its region.
(let ((x 2) (y 3)) (* x y)) ⇒ 6 (let ((x 2) (y 3)) (let ((x 7) (z (+ x y))) (* z x))) ⇒ 35See also named
let.
<bindings> must have the form:
((<variable1> <init1>) ...)where each <init> is an expression, and <body> is as described in baselib bodies.
The
let*form is similar tolet, but the <init>s are evaluated and bindings created sequentially from left to right, with the region of each binding including the bindings to its right as well as <body>. Thus the second <init> is evaluated in an environment in which the first binding is visible and initialized, and so on.(let ((x 2) (y 3)) (let* ((x 7) (z (+ x y))) (* z x))) ⇒ 70NOTE While the variables bound by aletexpression must be distinct, the variables bound by alet*expression need not be distinct.
<bindings> must have the form:
((<variable1> <init1>) ...)where each <init> is an expression, and <body> is as described in baselib bodies. Any variable must not appear more than once in the <variable>s.
The <variable>s are bound to fresh locations, the <init>s are evaluated in the resulting environment (in some unspecified order), each <variable> is assigned to the result of the corresponding <init>, the <body> is evaluated in the resulting environment, and the values of the last expression in <body> are returned. Each binding of a <variable> has the entire
letrecexpression as its region, making it possible to define mutually recursive procedures.(letrec ((even? (lambda (n) (if (zero? n) \schtrue (odd? (- n 1))))) (odd? (lambda (n) (if (zero? n) \schfalse (even? (- n 1)))))) (even? 88)) ⇒ #tIt should be possible to evaluate each <init> without assigning or referring to the value of any <variable>. In the most common uses of
letrec, all the <init>s arelambdas and the restriction is satisfied automatically. Another restriction is that the continuation of each <init> should not be invoked more than once.Implementation responsibilities: Implementations must detect references to a <variable> during the evaluation of the <init> expressions (using one particular evaluation order and order of evaluating the <init> expressions). If an implementation detects such a violation of the restriction, it must raise an exception with condition type
&assertion. Implementations may or may not detect that the continuation of each <init> is invoked more than once. However, if the implementation detects this, it must raise an exception with condition type&assertion.
<bindings> must have the form:
((<variable1> <init1>) ...)where each <init> is an expression, and <body> is as described in baselib bodies. Any variable must not appear more than once in the <variable>s.
The <variable>s are bound to fresh locations, each <variable> is assigned in left–to–right order to the result of evaluating the corresponding <init>, the <body> is evaluated in the resulting environment, and the values of the last expression in <body> are returned. Despite the left–to–right evaluation and assignment order, each binding of a <variable> has the entire
letrec*expression as its region, making it possible to define mutually recursive procedures.(letrec* ((p (lambda (x) (+ 1 (q (- x 1))))) (q (lambda (y) (if (zero? y) 0 (+ 1 (p (- y 1)))))) (x (p 5)) (y x)) y) ⇒ 5It must be possible to evaluate each <init> without assigning or referring to the value of the corresponding <variable> or the <variable> of any of the bindings that follow it in <bindings>. Another restriction is that the continuation of each <init> should not be invoked more than once.
Implementation responsibilities: Implementations must, during the evaluation of an <init> expression, detect references to the value of the corresponding <variable> or the <variable> of any of the bindings that follow it in <bindings>. If an implementation detects such a violation of the restriction, it must raise an exception with condition type
&assertion. Implementations may or may not detect that the continuation of each <init> is invoked more than once. However, if the implementation detects this, it must raise an exception with condition type&assertion.
<mv-bindings> must have the form:
((<formals1> <init1>) ...)where each <init> is an expression, and <body> is as described in baselib bodies. Any variable must not appear more than once in the set of <formals>.
The <init>s are evaluated in the current environment (in some unspecified order), and the variables occurring in the <formals> are bound to fresh locations containing the values returned by the <init>s, where the <formals> are matched to the return values in the same way that the <formals> in a
lambdaare matched to the arguments in a procedure call. Then, the <body> is evaluated in the extended environment, and the values of the last expression of <body> are returned. Each binding of a variable has <body> as its region. If the <formals> do not match, an exception with condition type&assertionis raised.(let-values (((a b) (values 1 2)) ((c d) (values 3 4))) (list a b c d)) ⇒ (1 2 3 4) (let-values (((a b . c) (values 1 2 3 4))) (list a b c)) ⇒ (1 2 (3 4)) (let ((a 'a) (b 'b) (x 'x) (y 'y)) (let-values (((a b) (values x y)) ((x y) (values a b))) (list a b x y))) ⇒ (x y a b)
<mv-bindings> must have the form:
((<formals1> <init1>) ...)where each <init> is an expression, and <body> is as described in baselib bodies. In each <formals>, any variable must not appear more than once.
The
let*-valuesform is similar tolet-values, but the <init>s are evaluated and bindings created sequentially from left to right, with the region including the bindings to its right as well as <body>. Thus the second <init> is evaluated in an environment in which the bindings of the first <formals> is visible and initialized, and so on.(let ((a 'a) (b 'b) (x 'x) (y 'y)) (let*-values (((a b) (values x y)) ((x y) (values a b))) (list a b x y))) ⇒ (x y x y)NOTE While all of the variables bound by alet-valuesexpression must be distinct, the variables bound by different <formals> of alet*-valuesexpression need not be distinct.
The <begin> keyword has two different roles, depending on its context:
- It may appear as a form in a <body>, <library body>, or <top-level body>, or directly nested in a
beginform that appears in a body. In this case, thebeginform must have the shape specified in the first header line. This use ofbeginacts as a splicing form: the forms inside the <body> are spliced into the surrounding body, as if thebeginwrapper were not actually present.A
beginform in a <body> or <library body> must be non–empty if it appears after the first <expression> within the body.- It may appear as an ordinary expression and must have the shape specified in the second header line. In this case, the <expression>s are evaluated sequentially from left to right, and the values of the last <expression> are returned. This expression type is used to sequence side effects such as assignments or input and output.
(define x 0) (begin (set! x 5) (+ x 1)) ⇒ 6 (begin (display "4 plus 1 equals ") (display (+ 4 1))) ⇒ unspecified -| 4 plus 1 equals 5
A predicate is a procedure that always returns a boolean value
(#t or #f). An equivalence predicate is the
computational analogue of a mathematical equivalence relation (it is
symmetric, reflexive, and transitive). Of the equivalence predicates
described in this section, eq? is the finest or most
discriminating, and equal? is the coarsest. The eqv?
predicate is slightly less discriminating than eq?.
The
eqv?procedure defines a useful equivalence relation on objects. Briefly, it returns#tif obj1 and obj2 should normally be regarded as the same object and#fotherwise. This relation is left slightly open to interpretation, but the following partial specification ofeqv?must hold for all implementations.The
eqv?procedure returns#tif one of the following holds:
- obj1 and obj2 are both booleans and are the same according to the
boolean=?procedure.- obj1 and obj2 are both symbols and are the same according to the
symbol=?procedure.- obj1 and obj2 are both exact number objects and are numerically equal (see
=).- obj1 and obj2 are both inexact number objects, are numerically equal (see
=), and yield the same results (in the sense ofeqv?) when passed as arguments to any other procedure that can be defined as a finite composition of Scheme's standard arithmetic procedures.- obj1 and obj2 are both characters and are the same character according to the
char=?procedure.- Both obj1 and obj2 are the empty list.
- obj1 and obj2 are objects such as pairs, vectors, bytevectors (library chapter “Bytevectors”), strings, hashtables, records (library chapter “Records”), ports (library section “Port I/O”), or hashtables (library chapter “Hash tables”) that refer to the same locations in the store.
- obj1 and obj2 are record–type descriptors that are specified to be
eqv?in library section “Procedural layer”.The
eqv?procedure returns#fif one of the following holds:
- obj1 and obj2 are of different types.
- obj1 and obj2 are booleans for which the
boolean=?procedure returns#f.- Obj1 and obj2 are symbols for which the
symbol=?procedure returns#f.- One of obj1 and obj2 is an exact number object but the other is an inexact number object.
- obj1 and obj2 are rational number objects for which the
=procedure returns#f.- Obj1 and obj2 yield different results (in the sense of
eqv?) when passed as arguments to any other procedure that can be defined as a finite composition of Scheme's standard arithmetic procedures.- obj1 and obj2 are characters for which the
char=?procedure returns#f.- One of obj1 and obj2 is the empty list, but the other is not.
- obj1 and obj2 are objects such as pairs, vectors, bytevectors (library chapter “Bytevectors”), strings, records (library chapter “Records”), ports (library section “Port I/O”), or hashtables (library chapter “Hashtables”) that refer to distinct locations.
- obj1 and obj2 are pairs, vectors, strings, or records, or hashtables, where the applying the same accessor (i.e.
car,cdr,vector-ref,string-ref, or record accessors) to both yields results for whicheqv?returns#f.- obj1 and obj2 are procedures that would behave differently (return different values or have different side effects) for some arguments.
NOTE Theeqv?procedure returning#twhen obj1 and obj2 are number objects does not imply that=would also return#twhen called with obj1 and obj2 as arguments.(eqv? 'a 'a) ⇒ #t (eqv? 'a 'b) ⇒ #f (eqv? 2 2) ⇒ #t (eqv? '() '()) ⇒ #t (eqv? 100000000 100000000) ⇒ #t (eqv? (cons 1 2) (cons 1 2)) ⇒ #f (eqv? (lambda () 1) (lambda () 2)) ⇒ #f (eqv? #f 'nil) ⇒ #fThe following examples illustrate cases in which the above rules do not fully specify the behavior of
eqv?. All that can be said about such cases is that the value returned byeqv?must be a boolean.(let ((p (lambda (x) x))) (eqv? p p)) ⇒ unspecified (eqv? "" "") ⇒ unspecified (eqv? '#() '#()) ⇒ unspecified (eqv? (lambda (x) x) (lambda (x) x)) ⇒ unspecified (eqv? (lambda (x) x) (lambda (y) y)) ⇒ unspecified (eqv? +nan.0 +nan.0) ⇒ unspecifiedThe next set of examples shows the use of
eqv?with procedures that have local state. Calls togen-countermust return a distinct procedure every time, since each procedure has its own internal counter. Calls togen-loserreturn procedures that behave equivalently when called. However,eqv?may not detect this equivalence.(define gen-counter (lambda () (let ((n 0)) (lambda () (set! n (+ n 1)) n)))) (let ((g (gen-counter))) (eqv? g g)) ⇒ unspecified (eqv? (gen-counter) (gen-counter)) ⇒ #f (define gen-loser (lambda () (let ((n 0)) (lambda () (set! n (+ n 1)) 27)))) (let ((g (gen-loser))) (eqv? g g)) ⇒ unspecified (eqv? (gen-loser) (gen-loser)) ⇒ unspecified (letrec ((f (lambda () (if (eqv? f g) 'both 'f))) (g (lambda () (if (eqv? f g) 'both 'g)))) (eqv? f g)) ⇒ unspecified (letrec ((f (lambda () (if (eqv? f g) 'f 'both))) (g (lambda () (if (eqv? f g) 'g 'both)))) (eqv? f g)) ⇒ #fImplementations may share structure between constants where appropriate. Furthermore, a constant may be copied at any time by the implementation so as to exist simultaneously in different sets of locations. Thus the value of
eqv?on constants is sometimes implementation–dependent.(eqv? '(a) '(a)) ⇒ unspecified (eqv? "a" "a") ⇒ unspecified (eqv? '(b) (cdr '(a b))) ⇒ unspecified (let ((x '(a))) (eqv? x x)) ⇒ #t
The
eq?predicate is similar toeqv?except that in some cases it is capable of discerning distinctions finer than those detectable byeqv?.The
eq?andeqv?predicates are guaranteed to have the same behavior on symbols, booleans, the empty list, pairs, procedures, non–empty strings, bytevectors, vectors, and records. The behavior ofeq?on number objects and characters is implementation–dependent, but it always returns either#tor#f, and returns#tonly wheneqv?would also return#t. Theeq?predicate may also behave differently fromeqv?on empty vectors, empty bytevectors, and empty strings.(eq? 'a 'a) ⇒ #t (eq? '(a) '(a)) ⇒ unspecified (eq? (list 'a) (list 'a)) ⇒ #f (eq? "a" "a") ⇒ unspecified (eq? "" "") ⇒ unspecified (eq? '() '()) ⇒ #t (eq? 2 2) ⇒ unspecified (eq? #\A #\A) ⇒ unspecified (eq? car car) ⇒ #t (let ((n (+ 2 3))) (eq? n n)) ⇒ unspecified (let ((x '(a))) (eq? x x)) ⇒ #t (let ((x '#())) (eq? x x)) ⇒ unspecified (let ((p (lambda (x) x))) (eq? p p)) ⇒ unspecified
The
equal?predicate returns#tif and only if the (possibly infinite) unfoldings of its arguments into regular trees are equal as ordered trees.The
equal?predicate treats pairs and vectors as nodes with outgoing edges, usesstring=?to compare strings, usesbytevector=?to compare bytevectors (stdlib bytevector), and useseqv?to compare other nodes.(equal? 'a 'a) ⇒ #t (equal? '(a) '(a)) ⇒ #t (equal? '(a (b) c) '(a (b) c)) ⇒ #t (equal? "abc" "abc") ⇒ #t (equal? 2 2) ⇒ #t (equal? (make-vector 5 'a) (make-vector 5 'a)) ⇒ #t (equal? '#vu8(1 2 3 4 5) (u8-list->bytevector '(1 2 3 4 5)) ⇒ #t (equal? (lambda (x) x) (lambda (y) y)) ⇒ unspecified (let* ((x (list 'a)) (y (list 'a)) (z (list x y))) (list (equal? z (list y x)) (equal? z (list x x)))) ⇒ (#t #t)NOTE Theequal?procedure must always terminate, even if its arguments contain cycles.
Return
#tif obj is a procedure, otherwise return#f.(procedure? car) ⇒ #t (procedure? 'car) ⇒ #f (procedure? (lambda (x) (* x x))) ⇒ #t (procedure? '(lambda (x) (* x x))) ⇒ #f
The procedures described here implement arithmetic that is generic over the numerical tower. The generic procedures described in this section accept both exact and inexact number objects as arguments, performing coercions and selecting the appropriate operations as determined by the numeric subtypes of their arguments.
The procedures listed below must return the mathematically correct exact result provided all their arguments are exact:
+ - *
max min abs
numerator denominator gcd
lcm floor ceiling
truncate round rationalize
real-part imag-part make-rectangular
The procedures listed below must return the correct exact result provided all their arguments are exact, and no divisors are zero:
/
div mod div-and-mod
div0 mod0 div0-and-mod0
Moreover, the procedure expt must return the correct exact result
provided its first argument is an exact real number object and its
second argument is an exact integer object.
The general rule is that the generic operations return the correct exact result when all of their arguments are exact and the result is mathematically well–defined, but return an inexact result when any argument is inexact. Exceptions to this rule include:
sqrt exp log
sin cos tan
asin acos atan
expt make-polar magnitude
angle
which may (but are not required to) return inexact results even when given exact arguments, as indicated in the specification of these procedures.
One general exception to the rule above is that an implementation may
return an exact result despite inexact arguments if that exact result
would be the correct result for all possible substitutions of exact
arguments for the inexact ones. An example is (* 1.0 0) which
may return either 0 (exact) or 0.0 (inexact).
The specification of the numerical operations is written as though infinities and NaNs are representable, and specifies many operations with respect to these number objects in ways that are consistent with the IEEE 754 standard for binary floating–point arithmetic.
An implementation of Scheme may or may not represent infinities and
NaNs; however, an implementation must raise a continuable exception with
condition type &no-infinities or &no-nans (respectively;
stdlib arithmetics flonum) whenever it is unable to represent an
infinity or NaN as specified. In this case, the continuation of the
exception handler is the continuation that otherwise would have received
the infinity or NaN value. This requirement also applies to conversions
between number objects and external representations, including the
reading of program source code.
Some operations are the semantic basis for several arithmetic procedures. The behavior of these operations is described in this section for later reference.
Scheme's operations for performing integer division rely on mathematical operations div, mod, div_0, and mod_0, that are defined as follows:
x_1 div x_2 = n_d
x_1 mod x_2 = x_m
where:
x_1 = n_d * x_2 + x_m
0 <= x_m < |x_2|
Examples:
123 div 10 = 12
123 mod 10 = 3
123 div -10 = -12
123 mod -10 = 3
-123 div 10 = -13
-123 mod 10 = 7
-123 div -10 = 13
-123 mod -10 = 7
x_1 div_0 x_2 = n_d
x_1 mod_0 x_2 = x_m
where:
x_1 = n_d * x_2 + x_m
-|x_2/2| <= x_m < |x_2/2|
Examples:
123 div_0 10 = 12
123 mod_0 10 = 3
123 div_0 -10 = -12
123 mod_0 -10 = 3
-123 div_0 10 = -12
-123 mod_0 10 = -3
-123 div_0 -10 = 12
-123 mod_0 -10 = -3
In general, the transcendental functions \log, \sin^(-1) (arcsine), \cos^(-1) (arccosine), and \tan^(-1) are multiply defined. The value of \log z is defined to be the one whose imaginary part lies in the range from -\pi (inclusive if -0.0 is distinguished, exclusive otherwise) to \pi (inclusive). \log 0 is undefined.
The value of \log z for non–real z is defined in terms of log on real numbers as:
log z = log |z| + (angle z) i
where angle z is the angle of z = a * e^(i * b) specified as:
angle z = b + 2 pi n
with -\pi <= angle z <= \pi and angle z = b + 2 pi n for some integer n.
With the one–argument version of \log defined this way, the
values of the two–argument–version of \log, sin^(-1) z,
cos^(-1) z, tan^(-1) z, and the two–argument version of
tan^(-1) are according to the following formulae:
log_b z = (log z)/(log b)
sin^(-1) z = -i log (i z + sqrt(1 - z^2))
cos^(-1) z = pi/2 - sin^(-1) z
tan^(-1) z = (log (1 + i z) - log (1 - i z)) / (2 i)
tan^(-1) x y = angle(x + yi)
The range of tan^(-1) x y is as in the following table. The asterisk (*) indicates that the entry applies to implementations that distinguish minus zero.
| y condition | x condition | range of result r
| |
|---|---|---|---|
| y = 0.0 | x > 0.0 | 0.0
| |
| * | y = +0.0 | x > 0.0 | +0.0
|
| * | y = -0.0 | x > 0.0 | -0.0
|
| y > 0.0 | x > 0.0 | 0.0 < r < pi/2
| |
| y > 0.0 | x = 0.0 | pi/2
| |
| y > 0.0 | x < 0.0 | pi/2 < r < pi
| |
| y = 0.0 | x < 0 | pi
| |
| * | y = +0.0 | x < 0.0 | pi
|
| * | y = -0.0 | x < 0.0 | -pi
|
| y < 0.0 | x < 0.0 | -pi< r < -pi/2
| |
| y < 0.0 | x = 0.0 | -pi/2
| |
| y < 0.0 | x > 0.0 | -pi/2 < r < 0.0
| |
| y = 0.0 | x = 0.0 | undefined
| |
| * | y = +0.0 | x = +0.0 | +0.0
|
| * | y = -0.0 | x = +0.0 | -0.0
|
| * | y = +0.0 | x = -0.0 | pi
|
| * | y = -0.0 | x = -0.0 | -pi
|
| * | y = +0.0 | x = 0 | pi/2
|
| * | y = -0.0 | x = 0 | -pi/2
|
These numerical type predicates can be applied to any kind of argument. They return
#tif the object is a number object of the named type, and#fotherwise. In general, if a type predicate is true of a number object then all higher type predicates are also true of that number object. Consequently, if a type predicate is false of a number object, then all lower type predicates are also false of that number object.If z is a complex number object, then
(real?z)is true if and only if(zero? (imag-partz))and(exact? (imag-partz))are both true.If x is a real number object, then
(rational?x)is true if and only if there exist exact integer objects k1 and k2 such that(=x(/k1 k2))and(= (numeratorx)k1)and(= (denominatorx)k2)are all true. Thus infinities and NaNs are not rational number objects.If q is a rational number object, then
(integer?q)is true if and only if(= (denominatorq) 1)is true. If q is not a rational number object, then(integer?q)is#f.Notice that the comparison function
=does not care about the exactness of its arguments:(= 1 1.1) ⇒ #t (= 1 #i1.1) ⇒ #tso, with the given definition, the only real numbers (according to
real?) that are not also rational numbers (according torational?) are infinities and NaNs. For the same reason, numbers are integers (according tointeger?) without respect for exactness.(complex? 3+4i) ⇒ #t (complex? 3) ⇒ #t (real? 3) ⇒ #t (real? -2.5+0.0i) ⇒ #f (real? -2.5+0i) ⇒ #t (real? -2.5) ⇒ #t (real? #e1e10) ⇒ #t (rational? 6/10) ⇒ #t (rational? 6/3) ⇒ #t (rational? 2) ⇒ #t (integer? 3+0i) ⇒ #t (integer? 3.0) ⇒ #t (integer? 8/4) ⇒ #t (number? +nan.0) ⇒ #t (complex? +nan.0) ⇒ #t (real? +nan.0) ⇒ #t (rational? +nan.0) ⇒ #f (complex? +inf.0) ⇒ #t (real? -inf.0) ⇒ #t (rational? -inf.0) ⇒ #f (integer? -inf.0) ⇒ #fNotice that ‘3.0+0i’ is an integer number according to
integer?, because the imaginary part is exact zero. ‘3.0+0.0i’ is a complex number because the imaginary part is inexact zero, and it is equal to ‘#i3.0+0i’; ‘3.0+0.0i’ is not integer according tointeger?, but it isinteger-valued?.(integer? 3.0+0i) ⇒ #t (integer? 3.0+0.0i) ⇒ #f (integer? #i3.0+0i) ⇒ #f (integer-valued? 3.0+0.0i) ⇒ #t (integer-valued? #i3.0+0i) ⇒ #tNOTE Except fornumber?, the behavior of these type predicates on inexact number objects is unreliable, because any inaccuracy may affect the result.
These numerical type predicates can be applied to any kind of argument. The
real-valued?procedure returns#tif the object is a number object and is equal in the sense of=to some real number object, or if the object is a NaN, or a complex number object whose real part is a NaN and whose imaginary part is zero in the sense ofzero?. Therational-valued?andinteger-valued?procedures return#tif the object is a number object and is equal in the sense of=to some object of the named type, and otherwise they return#f.(real-valued? +nan.0) ⇒ #t (real-valued? +nan.0+0i) ⇒ #t (real-valued? -inf.0) ⇒ #t (real-valued? 3) ⇒ #t (real-valued? -2.5+0.0i) ⇒ #t (real-valued? -2.5+0i) ⇒ #t (real-valued? -2.5) ⇒ #t (real-valued? #e1e10) ⇒ #t (rational-valued? +nan.0) ⇒ #f (rational-valued? -inf.0) ⇒ #f (rational-valued? 6/10) ⇒ #t (rational-valued? 6/10+0.0i) ⇒ #t (rational-valued? 6/10+0i) ⇒ #t (rational-valued? 6/3) ⇒ #t (integer-valued? 3+0i) ⇒ #t (integer-valued? 3+0.0i) ⇒ #t (integer-valued? 3.0) ⇒ #t (integer-valued? 3.0+0.0i) ⇒ #t (integer-valued? 8/4) ⇒ #tNOTE These procedures test whether a given number object can be coerced to the specified type without loss of numerical accuracy. Specifically, the behavior of these predicates differs from the behavior ofreal?,rational?, andinteger?on complex number objects whose imaginary part is inexact zero.NOTE The behavior of these type predicates on inexact number objects is unreliable, because any inaccuracy may affect the result.
These numerical predicates provide tests for the exactness of a quantity. For any number object, precisely one of these predicates is true.
(exact? 5) ⇒ #t (inexact? +inf.0) ⇒ #t (inexact? +nan.0) ⇒ #t
These procedures implement the natural one–to-one correspondence
between exact and inexact integer objects throughout an
implementation–dependent range. The inexact and exact
procedures are idempotent.
The
inexactprocedure returns an inexact representation of z. If inexact number objects of the appropriate type have bounded precision, then the value returned is an inexact number object that is nearest to the argument. If an exact argument has no reasonably close inexact equivalent, an exception with condition type&implementation-violationmay be raised.NOTE For a real number object whose magnitude is finite but so large that it has no reasonable finite approximation as an inexact number, a reasonably close inexact equivalent may be+inf.0or-inf.0. Similarly, the inexact representation of a complex number object whose components are finite may have infinite components.
The
exactprocedure returns an exact representation of z. The value returned is the exact number object that is numerically closest to the argument; in most cases, the result of this procedure should be numerically equal to its argument. If an inexact argument has no reasonably close exact equivalent, an exception with condition type&implementation-violationmay be raised.
These procedures return
#tif their arguments are (respectively): equal, monotonically increasing, monotonically decreasing, monotonically nondecreasing, or monotonically nonincreasing, and#fotherwise.Examples:
(= +inf.0 +inf.0) ⇒ #t (= -inf.0 +inf.0) ⇒ #f (= -inf.0 -inf.0) ⇒ #tfor any real number object x that is neither infinite nor NaN:
(< -inf.0 x +inf.0) ⇒ #t (> +inf.0 x -inf.0) ⇒ #tfor any number object z:
(= +nan.0 z) ⇒ #fFor any real number object x:
(< +nan.0 x) ⇒ #f (> +nan.0 x) ⇒ #fThese predicates must be transitive.
NOTE The traditional implementations of these predicates in Lisp–like languages are not transitive.NOTE While it is possible to compare inexact number objects using these predicates, the results may be unreliable because a small inaccuracy may affect the result; this is especially true of=andzero?(below).When in doubt, consult a numerical analyst.
These numerical predicates test a number object for a particular property, returning
#tor#f.
zero?- Tests if the number object is
=to zero.positive?- Tests whether it is greater than zero.
negative?- Tests whether it is less than zero.
odd?- Tests whether it is odd.
even?- Tests whether it is even.
finite?- Tests whether it is not an infinity and not a NaN.
infinite?- Tests whether it is an infinity.
nan?- Tests whether it is a NaN.
(zero? +0.0) ⇒ #t (zero? -0.0) ⇒ #t (zero? +nan.0) ⇒ #f (positive? +inf.0) ⇒ #t (negative? -inf.0) ⇒ #t (positive? +nan.0) ⇒ #f (negative? +nan.0) ⇒ #f (finite? +inf.0) ⇒ #f (finite? 5) ⇒ #t (finite? 5.0) ⇒ #t (infinite? 5.0) ⇒ #f (infinite? +inf.0) ⇒ #tNOTE As with the predicates above, the results may be unreliable because a small inaccuracy may affect the result.
These procedures return the maximum or minimum of their arguments.
(max 3 4) ⇒ 4 (max 3.9 4) ⇒ 4.0For any real number object x that is not a NaN:
(max +inf.0 x) ⇒ +inf.0 (min -inf.0 x) ⇒ -inf.0NOTE If any argument is inexact, then the result is also inexact (unless the procedure can prove that the inaccuracy is not large enough to affect the result, which is possible only in unusual implementations). Ifminormaxis used to compare number objects of mixed exactness, and the numerical value of the result cannot be represented as an inexact number object without loss of accuracy, then the procedure may raise an exception with condition type&implementation-restriction.
These procedures return the sum or product of their arguments.
(+ 3 4) ⇒ 7 (+ 3) ⇒ 3 (+) ⇒ 0 (+ +inf.0 +inf.0) ⇒ +inf.0 (+ +inf.0 -inf.0) ⇒ +nan.0 (* 4) ⇒ 4 (*) ⇒ 1 (* 5 +inf.0) ⇒ +inf.0 (* -5 +inf.0) ⇒ -inf.0 (* +inf.0 +inf.0) ⇒ +inf.0 (* +inf.0 -inf.0) ⇒ -inf.0 (* 0 +inf.0) ⇒ 0 or +nan.0 (* 0 +nan.0) ⇒ 0 or +nan.0 (* 1.0 0) ⇒ 0 or 0.0For any real number object x that is neither infinite nor NaN:
(+ +inf.0 x) ⇒ +inf.0 (+ -inf.0 x) ⇒ -inf.0For any real number object x:
(+ +nan.0 x) ⇒ +nan.0For any real number object x that is not an exact 0:
(* +nan.0 x) ⇒ +nan.0If any of these procedures are applied to mixed non–rational real and non–real complex arguments, they either raise an exception with condition type
&implementation-restrictionor return an unspecified number object.Implementations that distinguish
-0.0should adopt behavior consistent with the following examples:(+ 0.0 -0.0) ⇒ 0.0 (+ -0.0 0.0) ⇒ 0.0 (+ 0.0 0.0) ⇒ 0.0 (+ -0.0 -0.0) ⇒ -0.0
With two or more arguments, this procedures returns the difference of its arguments, associating to the left. With one argument, however, it returns the additive inverse of its argument.
(- 3 4) ⇒ -1 (- 3 4 5) ⇒ -6 (- 3) ⇒ -3 (- +inf.0 +inf.0) ⇒ +nan.0If this procedure is applied to mixed non–rational real and non–real complex arguments, it either raises an exception with condition type
&implementation-restrictionor returns an unspecified number object.Implementations that distinguish -0.0 should adopt behavior consistent with the following examples:
(- 0.0) ⇒ -0.0 (- -0.0) ⇒ 0.0 (- 0.0 -0.0) ⇒ 0.0 (- -0.0 0.0) ⇒ -0.0 (- 0.0 0.0) ⇒ 0.0 (- -0.0 -0.0) ⇒ 0.0
If all of the arguments are exact, then the divisors must all be nonzero. With two or more arguments, this procedure returns the quotient of its arguments, associating to the left. With one argument, however, it returns the multiplicative inverse of its argument.
(/ 3 4 5) ⇒ 3/20 (/ 3) ⇒ 1/3 (/ 0.0) ⇒ +inf.0 (/ 1.0 0) ⇒ +inf.0 (/ -1 0.0) ⇒ -inf.0 (/ +inf.0) ⇒ 0.0 (/ 0 0) ⇒ exception &assertion (/ 3 0) ⇒ exception &assertion (/ 0 3.5) ⇒ 0.0 (/ 0 0.0) ⇒ +nan.0 (/ 0.0 0) ⇒ +nan.0 (/ 0.0 0.0) ⇒ +nan.0If this procedure is applied to mixed non–rational real and non–real complex arguments, it either raises an exception with condition type
&implementation-restrictionor returns an unspecified number object.
These procedures implement number–theoretic integer division and return the results of the corresponding mathematical operations specified in baselib math semantics integer. If x1 and x2 are exact, x2 must be nonzero. In the cases where the mathematical requirements in baselib math semantics integer cannot be satisfied by any number object, either an exception is raised with condition type
&implementation-restriction, or unspecified number objects (one for fordiv,mod,div0andmod0, two fordiv-and-modanddiv0-and-mod0) are returned.(div x1 x2) ⇒ x1 div x2 (mod x1 x2) ⇒ x1 mod x2 (div-and-mod x1 x2) ⇒ x1 div x2, x1 mod x2 ;; two return values (div0 x1 x2) ⇒ x1 div_0 x2 (mod0 x1 x2) ⇒ x1 mod_0 x2 (div0-and-mod0 x1 x2) ⇒ x1 div_0 x2, x1 mod_0 x2 ;; two return values
These procedures return the greatest common divisor or least common multiple of their arguments. The result is always non–negative.
(gcd 32 -36) ⇒ 4 (gcd) ⇒ 0 (lcm 32 -36) ⇒ 288 (lcm 32.0 -36) ⇒ 288.0 (lcm) ⇒ 1
These procedures return the numerator or denominator of their argument; the result is computed as if the argument was represented as a fraction in lowest terms. The denominator is always positive. The denominator of
0is defined to be1.(numerator (/ 6 4)) ⇒ 3 (denominator (/ 6 4)) ⇒ 2 (denominator (inexact (/ 6 4))) ⇒ 2.0
These procedures return inexact integer objects for inexact arguments that are not infinities or NaNs, and exact integer objects for exact rational arguments.
floor- Returns the largest integer object not larger than x.
ceiling- Returns the smallest integer object not smaller than x.
truncate- Returns the integer object closest to x whose absolute value is not larger than the absolute value of x.
round- Returns the closest integer object to x, rounding to even when x represents a number halfway between two integers (this conforms to IEEE 754 round to nearest mode).
If the argument to one of these procedures is inexact, then the result is also inexact. If an exact value is needed, the result should be passed to the
exactprocedure.Although infinities and NaNs are not integer objects, these procedures return an infinity when given an infinity as an argument, and a NaN when given a NaN.
(floor -4.3) ⇒ -5.0 (ceiling -4.3) ⇒ -4.0 (truncate -4.3) ⇒ -4.0 (round -4.3) ⇒ -4.0 (floor 3.5) ⇒ 3.0 (ceiling 3.5) ⇒ 4.0 (truncate 3.5) ⇒ 3.0 (round 3.5) ⇒ 4.0 (round 7/2) ⇒ 4 (round 7) ⇒ 7 (floor +inf.0) ⇒ +inf.0 (ceiling -inf.0) ⇒ -inf.0 (round +nan.0) ⇒ +nan.0
The
rationalizeprocedure returns a number object representing the simplest rational number differing from x1 by no more than x2.A rational number r_1 is simpler than another rational number r_2 if r_1 = p_1/q_1 and r_2 = p_2/q_2 (in lowest terms) and |p_1| <= |p_2| and |q_1| <= |q_2|. Thus 3/5 is simpler than 4/7.
Although not all rationals are comparable in this ordering (consider 2/7 and 3/5) any interval contains a rational number that is simpler than every other rational number in that interval (the simpler 2/5 lies between 2/7 and 3/5).
Note that 0 = 0/1 is the simplest rational of all.
(rationalize (exact .3) 1/10) ⇒ 1/3 (rationalize .3 1/10) ⇒ #i1/3 ; approximately (rationalize +inf.0 3) ⇒ +inf.0 (rationalize +inf.0 +inf.0) ⇒ +nan.0 (rationalize 3 +inf.0) ⇒ 0.0The first two examples hold only in implementations whose inexact real number objects have sufficient precision.
These procedures compute the usual transcendental functions.
The
expprocedure computes the base–e exponential of z.The
logprocedure with a single argument computes the natural logarithm of z (not the base–10 logarithm);(logz1 z2)computes the base–z2 logarithm of z1.The
asin,acos, andatanprocedures compute arcsine, arccosine, and arctangent, respectively. The two–argument variant ofatancomputes:(angle (make-rectangular x2 x1))These procedures may return inexact results even when given exact arguments.
(exp +inf.0) ⇒ +inf.0 (exp -inf.0) ⇒ 0.0 (log +inf.0) ⇒ +inf.0 (log 0.0) ⇒ -inf.0 (log 0) ⇒ exception &assertion (log -inf.0) ⇒ +inf.0+3.141592653589793i ; approximately (atan -inf.0) ⇒ -1.5707963267948965 ; approximately (atan +inf.0) ⇒ 1.5707963267948965 ; approximately (log -1.0+0.0i) ⇒ 0.0+3.141592653589793i ; approximately (log -1.0-0.0i) ⇒ 0.0-3.141592653589793i ; approximately if -0.0 is distinguished
Return the principal square root of z. For rational z, the result has either positive real part, or zero real part and non–negative imaginary part. With \log defined as in baselib math semantics trascend, the value of
(sqrtz)could be expressed as e^((\log z)/2).The
sqrtprocedure may return an inexact result even when given an exact argument.(sqrt -5) ⇒ 0.0+2.23606797749979i ; approximately (sqrt +inf.0) ⇒ +inf.0 (sqrt -inf.0) ⇒ +inf.0i
The
exact-integer-sqrtprocedure returns two non–negative exact integer objects s and r where k = s^2 + r and k < (s+1)^2.(exact-integer-sqrt 4) ⇒ 2 0 ; two return values (exact-integer-sqrt 5) ⇒ 2 1 ; two return values
Return z1 raised to the power z2. For non–zero z1, this is e^(z_2 \log z_1). 0.0^z is 1.0 if z = 0.0, and 0.0 if
(real-partz)is positive. For other cases in which the first argument is zero, either an exception is raised with condition type&implementation-restriction, or an unspecified number object is returned.For an exact real number object z1 and an exact integer object z2,
(exptz1 z2)must return an exact result. For all other values of z1 and z2,(exptz1 z2)may return an inexact result, even when both z1 and z2 are exact.(expt 5 3) ⇒ 125 (expt 5 -3) ⇒ 1/125 (expt 5 0) ⇒ 1 (expt 0 5) ⇒ 0 (expt 0 5+.0000312i) ⇒ 0.0 (expt 0 -5) ⇒ unspecified (expt 0 -5+.0000312i) ⇒ unspecified (expt 0 0) ⇒ 1 (expt 0.0 0.0) ⇒ 1.0
Suppose a_1, a_2, a_3, and a_4 are real numbers, and c is a complex number such that the following holds:
c = a_1 + a_2 i = a_3 e^(i a_4)Then, if x1, x2, x3, and x4 are number objects representing a_1, a_2, a_3, and a_4, respectively,
(make-rectangularx1 x2)returns c, and(make-polarx3 x4)returns c.(make-rectangular 1.1 2.2) ⇒ 1.1+2.2i ; approximately (make-polar 1.1 2.2) ⇒ 1.1@2.2 ; approximatelyConversely, if -\pi <= a_4 <= \pi, and if z is a number object representing c, then
(real-partz)returns a_1,(imag-partz)returns a_2,(magnitudez)returns a_3, and(anglez)returns a_4.(real-part 1.1+2.2i) ⇒ 1.1 ; approximately (imag-part 1.1+2.2i) ⇒ 2.2 ; approximately (magnitude 1.1@2.2) ⇒ 1.1 ; approximately (angle 1.1@2.2) ⇒ 2.2 ; approximately (angle -1.0) ⇒ 3.141592653589793 ; approximately (angle -1.0+0.0i) ⇒ 3.141592653589793 ; approximately (angle -1.0-0.0i) ⇒ -3.141592653589793 ; approximately if -0.0 is distinguished (angle +inf.0) ⇒ 0.0 (angle -inf.0) ⇒ 3.141592653589793 ; approximatelyMoreover, suppose x1, x2 are such that either x1 or x2 is an infinity, then
(make-rectangular x1 x2) ⇒ z (magnitude z) ⇒ +inf.0The
make-polar,magnitude, andangleprocedures may return inexact results even when given exact arguments.(angle -1) ⇒ 3.141592653589793 ; approximately
The
number->stringprocedure takes a number object and a radix and returns as a string an external representation of the given number object in the given radix such that:(let ((number z) (radix radix)) (eqv? (string->number (number->string number radix) radix) number))is true. If no possible result makes this expression true, an exception with condition type
&implementation-restrictionis raised.radix must be an exact integer object, either 2, 8, 10, or 16. If omitted, radix defaults to 10. If a precision is specified, then z must be an inexact complex number object, precision must be an exact positive integer object, and radix must be 10.
NOTE The error case can occur only when z is not a complex number object or is a complex number object with a non–rational real or imaginary part.If a precision is specified, then the representations of the inexact real components of the result, unless they are infinite or NaN, specify an explicit <mantissa width> p, and p is the least p >= precision for which the above expression is true.
If z is inexact, the radix is 10, and the above expression and condition can be satisfied by a result that contains a decimal point, then the result contains a decimal point and is expressed using the minimum number of digits (exclusive of exponent, trailing zeroes, and mantissa width) needed to make the above expression and condition true; otherwise the format of the result is unspecified.
The result returned by
number->stringnever contains an explicit radix prefix.
Return a number object with maximally precise representation expressed by the given string.
radix must be an exact integer object, either 2, 8, 10, or 16. If supplied, radix is a default radix that may be overridden by an explicit radix prefix in string (e.g.
#o177). If radix is not supplied, then the default radix is 10.If string is not a syntactically valid notation for a number object or a notation for a rational number object with a zero denominator, then
string->numberreturns#f.(string->number "100") ⇒ 100 (string->number "100" 16) ⇒ 256 (string->number "1e2") ⇒ 100.0 (string->number "0/0") ⇒ #f (string->number "+inf.0") ⇒ +inf.0 (string->number "-inf.0") ⇒ -inf.0 (string->number "+nan.0") ⇒ +nan.0NOTE Thestring->numberprocedure always returns a number object or#f; it never raises an exception.
The standard boolean objects for true and false have external
representations #t and #f. However, of all objects, only
#f counts as false in conditional expressions.
NOTE Programmers accustomed to other dialects of Lisp should be aware that Scheme distinguishes both#fand the empty list from each other and from the symbolnil.
Return
#tif obj is#f, or#fotherwise.(not #t) ⇒ #f (not 3) ⇒ #f (not (list 3)) ⇒ #f (not #f) ⇒ #t (not '()) ⇒ #f (not (list)) ⇒ #f (not 'nil) ⇒ #f
Return
#tif obj is either#tor#f, or#fotherwise.(boolean? #f) ⇒ #t (boolean? 0) ⇒ #f (boolean? '()) ⇒ #f
A pair is a compound structure with two fields called the car and
cdr fields (for historical reasons). Pairs are created by the procedure
cons. The car and cdr fields are accessed by the procedures
car and cdr.
Pairs are used primarily to represent lists. A list can be defined recursively as either the empty list or a pair whose cdr is a list. More precisely, the set of lists is defined as the smallest set X such that:
The objects in the car fields of successive pairs of a list are the elements of the list. For example, a two–element list is a pair whose car is the first element and whose cdr is a pair whose car is the second element and whose cdr is the empty list. The length of a list is the number of elements, which is the same as the number of pairs.
The empty list is a special object of its own type. It is not a pair. It has no elements and its length is zero.
NOTE The above definitions imply that all lists have finite length and are terminated by the empty list.
A chain of pairs not ending in the empty list is called an improper list. Note that an improper list is not a list. The list and dotted notations can be combined to represent improper lists:
(a b c . d)
is equivalent to
(a . (b . (c . d)))
Whether a given pair is a list depends upon what is stored in the cdr field.
Return
#tif obj is a pair,#fotherwise.(pair? '(a . b)) ⇒ #t (pair? '(a b c)) ⇒ #t (pair? '()) ⇒ #f (pair? '#(a b)) ⇒ #f
Return a newly allocated pair whose car is obj1 and whose cdr is obj2. The pair is guaranteed to be different (in the sense of
eqv?) from every existing object.(cons 'a '()) ⇒ (a) (cons '(a) '(b c d)) ⇒ ((a) b c d) (cons "a" '(b c)) ⇒ ("a" b c) (cons 'a 3) ⇒ (a . 3) (cons '(a b) 'c) ⇒ ((a b) . c)
Return the contents of the car field of pair.
(car '(a b c)) ⇒ a (car '((a) b c d)) ⇒ (a) (car '(1 . 2)) ⇒ 1 (car '()) ⇒ exception &assertion
Return the contents of the cdr field of pair.
(cdr '((a) b c d)) ⇒ (b c d) (cdr '(1 . 2)) ⇒ 2 (cdr '()) ⇒ exception &assertion
These procedures are compositions of
carandcdr, where for examplecaddrcould be defined by(define caddr (lambda (x) (car (cdr (cdr x)))))Arbitrary compositions, up to four deep, are provided. There are twenty–eight of these procedures in all.
Return
#tif obj is a list,#fotherwise. By definition, all lists are chains of pairs that have finite length and are terminated by the empty list.(list? '(a b c)) ⇒ #t (list? '()) ⇒ #t (list? '(a . b)) ⇒ #f
Return a newly allocated list of its arguments.
(list 'a (+ 3 4) 'c) ⇒ (a 7 c) (list) ⇒ ()
Return the length of list.
(length '(a b c)) ⇒ 3 (length '(a (b) (c d e))) ⇒ 3 (length '()) ⇒ 0
Return a possibly improper list consisting of the elements of the first list followed by the elements of the other lists, with obj as the cdr of the final pair. An improper list results if obj is not a list.
(append '(x) '(y)) ⇒ (x y) (append '(a) '(b c d)) ⇒ (a b c d) (append '(a (b)) '((c))) ⇒ (a (b) (c)) (append '(a b) '(c . d)) ⇒ (a b c . d) (append '() 'a) ⇒ aIf
appendconstructs a non–empty chain of pairs, it is always newly allocated. If no pairs are allocated, obj is returned.R6RS mandates that this function requires at least the argument obj; it is illegal to call this function with no arguments. Despite this: The Scheme implementations supported by Nausicaa (Larceny, Ikarus, Mosh, Ypsilon) allow calling this function with no arguments, in which case the return value is the empty string. The(nausicaa)language will ensure this behaviour. (Fri Jun 5, 2009)
Return a newly allocated list consisting of the elements of list in reverse order.
(reverse '(a b c)) ⇒ (c b a) (reverse '(a (b c) d (e (f)))) ⇒ ((e (f)) d (b c) a)
list should be a list of size at least k. Return the subchain of pairs of list obtained by omitting the first k elements.
(list-tail '(a b c d) 2) ⇒ (c d)Implementation responsibilities: The implementation must check that list is a chain of pairs whose length is at least k. It should not check that it is a chain of pairs beyond this length.
list must be a list whose length is at least k + 1. The
list-tailprocedure returns the kth element of list.(list-ref '(a b c d) 2) ⇒ cImplementation responsibilities: The implementation must check that list is a chain of pairs whose length is at least k + 1. It should not check that it is a list of pairs beyond this length.
The lists should all have the same length. proc should accept as many arguments as there are lists and return a single value. proc should not mutate any of the lists.
The
mapprocedure applies proc element–wise to the elements of the lists and returns a list of the results, in order. proc is always called in the same dynamic environment asmapitself. The order in which proc is applied to the elements of the lists is unspecified. If multiple returns occur frommap, the values returned by earlier returns are not mutated.(map cadr '((a b) (d e) (g h))) ⇒ (b e h) (map (lambda (n) (expt n n)) '(1 2 3 4 5)) ⇒ (1 4 27 256 3125) (map + '(1 2 3) '(4 5 6)) ⇒ (5 7 9) (let ((count 0)) (map (lambda (ignored) (set! count (+ count 1)) count) '(a b))) ⇒ (1 2) or (2 1)Implementation responsibilities: The implementation should check that the lists all have the same length. The implementation must check the restrictions on proc to the extent performed by applying it as described. An implementation may check whether proc is an appropriate argument before applying it.
The lists should all have the same length. proc should accept as many arguments as there are lists. proc should not mutate any of the lists.
The
for-eachprocedure applies proc element–wise to the elements of the lists for its side effects, in order from the first elements to the last. proc is always called in the same dynamic environment asfor-eachitself. The return values offor-eachare unspecified.(let ((v (make-vector 5))) (for-each (lambda (i) (vector-set! v i (* i i))) '(0 1 2 3 4)) v) ⇒ #(0 1 4 9 16) (for-each (lambda (x) x) '(1 2 3 4)) ⇒ unspecified (for-each even? '()) ⇒ unspecifiedImplementation responsibilities: The implementation should check that the lists all have the same length. The implementation must check the restrictions on proc to the extent performed by applying it as described. An implementation may check whether proc is an appropriate argument before applying it.
NOTE Implementations offor-eachmay or may not tail–call proc on the last elements.
Symbols are objects whose usefulness rests on the fact that two symbols
are identical (in the sense of eq?, eqv? and
equal?) if and only if their names are spelled the same way. A
symbol literal is formed using quote.
Return
#tif obj is a symbol,#fotherwise.(symbol? 'foo) ⇒ #t (symbol? (car '(a b))) ⇒ #t (symbol? "bar") ⇒ #f (symbol? 'nil) ⇒ #t (symbol? '()) ⇒ #f (symbol? #f) ⇒ #f
Return the name of symbol as an immutable string.
(symbol->string 'flying-fish) ⇒ "flying-fish" (symbol->string 'Martin) ⇒ "Martin" (symbol->string (string->symbol "Malvina")) ⇒ "Malvina"
Return
#tif the symbols are the same, i.e., if their names are spelled the same.
Return the symbol whose name is string.
(eq? 'mISSISSIppi 'mississippi) ⇒ #f (string->symbol "mISSISSIppi") ⇒ the symbol with name "mISSISSIppi" (eq? 'bitBlt (string->symbol "bitBlt")) ⇒ #t (eq? 'JollyWog (string->symbol (symbol->string 'JollyWog))) ⇒ #t (string=? "K. Harper, M.D." (symbol->string (string->symbol "K. Harper, M.D."))) ⇒ #t
The characters are objects that represent Unicode scalar values.
Unicode defines a standard mapping between sequences of Unicode scalar values (integers in the range 0 to#x10FFFF, excluding the range#xD800to#xDFFF) in the latest version of the standard and human–readable “characters”.More precisely, Unicode distinguishes between glyphs, which are printed for humans to read, and characters, which are abstract entities that map to glyphs (sometimes in a way that's sensitive to surrounding characters). Furthermore, different sequences of scalar values sometimes correspond to the same character. The relationships among scalar, characters, and glyphs are subtle and complex.
Despite this complexity, most things that a literate human would call a “character” can be represented by a single Unicode scalar value (although several sequences of Unicode scalar values may represent that same character). For example, Roman letters, Cyrillic letters, Hebrew consonants, and most Chinese characters fall into this category.
Unicode scalar values exclude the range
#xD800to#xDFFF, which are part of the range of Unicode code points. However, the Unicode code points in this range, the so–called surrogates, are an artifact of the UTF–16 encoding, and can only appear in specific Unicode encodings, and even then only in pairs that encode scalar values. Consequently, all characters represent code points, but the surrogate code points do not have representations as characters.
sv must be a Unicode scalar value, i.e., a non–negative exact integer object in
[0, #xD7FF] union [#xE000, #x10FFFF].Given a character,
char->integerreturns its Unicode scalar value as an exact integer object. For a Unicode scalar value sv,integer->charreturns its associated character.(integer->char 32) ⇒ #\space (char->integer (integer->char 5000)) ⇒ 5000 (integer->char #\xD800) ⇒ exception &assertion
These procedures impose a total ordering on the set of characters according to their Unicode scalar values.
(char<? #\z #\Z) ⇒ #f
Strings are sequences of characters. The length of a string is the number of characters that it contains. This number is fixed when the string is created. The valid indices of a string are the integers less than the length of the string. The first character of a string has index 0, the second has index 1, and so on.
Return a newly allocated string of length k. If char is given, then all elements of the string are initialized to char, otherwise the contents of the string are unspecified.
Return the number of characters in the given string as an exact integer object.
k must be a valid index of string. The
string-refprocedure returns character k of string using zero–origin indexing.NOTE Implementors should makestring-refrun in constant time.
Return
#tif the strings are the same length and contain the same characters in the same positions. Otherwise, thestring=?procedure returns#f.(string=? "Strause" "Strasse") ⇒ #f
These procedures are the lexicographic extensions to strings of the corresponding orderings on characters. For example,
string<?is the lexicographic ordering on strings induced by the orderingchar<?on characters. If two strings differ in length but are the same up to the length of the shorter string, the shorter string is considered to be lexicographically less than the longer string.(string<? "z" "a") ⇒ #t (string<? "z" "zz") ⇒ #t (string<? "z" "Z") ⇒ #f
string must be a string, and start and end must be exact integer objects satisfying:
0 <= start <= end <= (string-length string)The
substringprocedure returns a newly allocated string formed from the characters of string beginning with index start (inclusive) and ending with index end (exclusive).
Return a newly allocated string whose characters form the concatenation of the given strings.
list must be a list of characters.
The
string->listprocedure returns a newly allocated list of the characters that make up the given string.The
list->stringprocedure returns a newly allocated string formed from the characters in list.The
string->listandlist->stringprocedures are inverses so far asequal?is concerned.
The strings must all have the same length. proc should accept as many arguments as there are strings.
The
string-for-eachprocedure applies proc element–wise to the characters of the strings for its side effects, in order from the first characters to the last. proc is always called in the same dynamic environment asstring-for-eachitself. The return values ofstring-for-eachare unspecified.Analogous to
for-each.Implementation responsibilities: The implementation must check the restrictions on proc to the extent performed by applying it as described. An implementation may check whether proc is an appropriate argument before applying it.
Vectors are heterogeneous structures whose elements are indexed by integers. A vector typically occupies less space than a list of the same length, and the average time needed to access a randomly chosen element is typically less for the vector than for the list.
The length of a vector is the number of elements that it contains. This number is a non–negative integer that is fixed when the vector is created. The valid indices of a vector are the exact non–negative integer objects less than the length of the vector. The first element in a vector is indexed by zero, and the last element is indexed by one less than the length of the vector.
Like list constants, vector constants must be quoted:
'#(0 (2 2 2 2) "Anna") ⇒ #(0 (2 2 2 2) "Anna")
Return a newly allocated vector of k elements. If a second argument is given, then each element is initialized to fill. Otherwise the initial contents of each element is unspecified.
Return a newly allocated vector whose elements contain the given arguments. Analogous to
list.(vector 'a 'b 'c) ⇒ #(a b c)
Return the number of elements in vector as an exact integer object.
k must be a valid index of vector. The
vector-refprocedure returns the contents of element k of vector.(vector-ref '#(1 1 2 3 5 8 13 21) 5) ⇒ 8
k must be a valid index of vector. The
vector-set!procedure stores obj in element k of vector, and returns unspecified.Passing an immutable vector to
vector-set!should cause an exception with condition type&assertionto be raised.(let ((vec (vector 0 '(2 2 2 2) "Anna"))) (vector-set! vec 1 '("Sue" "Sue")) vec) ⇒ #(0 ("Sue" "Sue") "Anna") (vector-set! '#(0 1 2) 1 "doe") ⇒ unspecified ;; constant vector ;; should raise exception &assertion
The
vector->listprocedure returns a newly allocated list of the objects contained in the elements of vector.The
list->vectorprocedure returns a newly created vector initialized to the elements of the list list.(vector->list '#(dah dah didah)) ⇒ (dah dah didah) (list->vector '(dididit dah)) ⇒ #(dididit dah)
Store fill in every element of vector and returns unspecified.
The vectors must all have the same length. proc should accept as many arguments as there are vectors and return a single value.
The
vector-mapprocedure applies proc element–wise to the elements of the vectors and returns a vector of the results, in order. proc is always called in the same dynamic environment asvector-mapitself. The order in which proc is applied to the elements of the vectors is unspecified. If multiple returns occur fromvector-map, the return values returned by earlier returns are not mutated.Analogous to
map.Implementation responsibilities: The implementation must check the restrictions on proc to the extent performed by applying it as described. An implementation may check whether proc is an appropriate argument before applying it.
The vectors must all have the same length. proc should accept as many arguments as there are vectors. The
vector-for-eachprocedure applies proc element–wise to the elements of the vectors for its side effects, in order from the first elements to the last. proc is always called in the same dynamic environment asvector-for-eachitself. The return values ofvector-for-eachare unspecified.Analogous to
for-each.Implementation responsibilities: The implementation must check the restrictions on proc to the extent performed by applying it as described. An implementation may check whether proc is an appropriate argument before applying it.
who must be a string or a symbol or
#f. message must be a string. The irritants are arbitrary objects.These procedures raise an exception. The
errorprocedure should be called when an error has occurred, typically caused by something that has gone wrong in the interaction of the program with the external world or the user. Theassertion-violationprocedure should be called when an invalid call to a procedure was made, either passing an invalid number of arguments, or passing an argument that it is not specified to handle.The who argument should describe the procedure or operation that detected the exception. The message argument should describe the exceptional situation. The irritants should be the arguments to the operation that detected the operation.
The condition object provided with the exception has the following condition types:
- If who is not
#f, the condition has condition type&who, with who as the value of its field. In that case, who should be the name of the procedure or entity that detected the exception. If it is#f, the condition does not have condition type&who.- The condition has condition type
&message, with message as the value of its field.- The condition has condition type
&irritants, and its field has as its value a list of the irritants.Moreover, the condition created by
errorhas condition type&error, and the condition created byassertion-violationhas condition type&assertion.(define (fac n) (if (not (integer-valued? n)) (assertion-violation 'fac "non-integral argument" n)) (if (negative? n) (assertion-violation 'fac "negative argument" n)) (letrec ((loop (lambda (n r) (if (zero? n) r (loop (- n 1) (* r n)))))) (loop n 1))) (fac 5) ⇒ 120 (fac 4.5) ⇒ exception &assertion (fac -3) ⇒ exception &assertion
An
assertform is evaluated by evaluating <expression>. If <expression> returns a true value, that value is returned from theassertexpression. If <expression> returns#f, an exception with condition types&assertionand&messageis raised. The message provided in the condition object is implementation–dependent.NOTE Implementations should exploit the fact thatassertis syntax to provide as much information as possible about the location of the assertion failure.
This chapter describes various primitive procedures which control the flow of program execution in special ways.
rest-args must be a list. proc should accept n arguments, where n is number of args plus the length of rest-args. The
applyprocedure calls proc with the elements of the list:(append (list arg1 ...) rest-args)as the actual arguments.
If a call to
applyoccurs in a tail context, the call to proc is also in a tail context.(apply + (list 3 4)) ⇒ 7 (define compose (lambda (f g) (lambda args (f (apply g args))))) ((compose sqrt *) 12 75) ⇒ 30
proc should accept one argument. The procedure
call/cc(which is the same as the procedurecall-with-current-continuation) packages the current continuation as an “escape procedure” and passes it as an argument to proc.The escape procedure is a Scheme procedure that, if it is later called, will abandon whatever continuation is in effect at that later time and will instead reinstate the continuation that was in effect when the escape procedure was created.
Calling the escape procedure may cause the invocation of before and after procedures installed using
dynamic-wind.The escape procedure accepts the same number of arguments as the continuation of the original call to
call/cc.The escape procedure that is passed to proc has unlimited extent just like any other procedure in Scheme. It may be stored in variables or data structures and may be called as many times as desired.
If a call to
call/ccoccurs in a tail context, the call to proc is also in a tail context.The following examples show only some ways in which
call/ccis used. If all real uses were as simple as these examples, there would be no need for a procedure with the power ofcall/cc.(call-with-current-continuation (lambda (exit) (for-each (lambda (x) (if (negative? x) (exit x))) '(54 0 37 -3 245 19)) #t)) ⇒ -3 (define list-length (lambda (obj) (call-with-current-continuation (lambda (return) (letrec ((r (lambda (obj) (cond ((null? obj) 0) ((pair? obj) (+ (r (cdr obj)) 1)) (else (return #f)))))) (r obj)))))) (list-length '(1 2 3 4)) ⇒ 4 (list-length '(a b . c)) ⇒ #f (call-with-current-continuation procedure?) ⇒ #tNOTE Calling an escape procedure reenters the dynamic extent of the call tocall/cc, and thus restores its dynamic environment.
Delivers all of its arguments to its continuation. The
valuesprocedure might be defined as follows:(define (values . things) (call-with-current-continuation (lambda (cont) (apply cont things))))The continuations of all non–final expressions within a sequence of expressions, such as in
lambda,begin,let,let*,letrec,letrec*,let-values,let*-values,case, andcondforms, usually take an arbitrary number of values.Except for these and the continuations created by
call-with-values,let-values, andlet*-values, continuations implicitly accepting a single value, such as the continuations of <operator> and <operand>s of procedure calls or the <test> expressions in conditionals, take exactly one value. The effect of passing an inappropriate number of values to such a continuation is undefined.
producer must be a procedure and should accept zero arguments. consumer must be a procedure and should accept as many values as producer returns. The
call-with-valuesprocedure calls producer with no arguments and a continuation that, when passed some values, calls the consumer procedure with those values as arguments. The continuation for the call to consumer is the continuation of the call tocall-with-values.(call-with-values (lambda () (values 4 5)) (lambda (a b) b)) ⇒ 5 (call-with-values * -) ⇒ -1If a call to
call-with-valuesoccurs in a tail context, the call to consumer is also in a tail context.Implementation responsibilities: After producer returns, the implementation must check that consumer accepts as many values as consumer has returned.
before, thunk, and after must be procedures, and each should accept zero arguments. These procedures may return any number of values.
The
dynamic-windprocedure calls thunk without arguments, returning the results of this call. Moreover,dynamic-windcalls before without arguments whenever the dynamic extent of the call to thunk is entered, and after without arguments whenever the dynamic extent of the call to thunk is exited. Thus, in the absence of calls to escape procedures created bycall/cc,dynamic-windcalls before, thunk, and after, in that order.While the calls to before and after are not considered to be within the dynamic extent of the call to thunk, calls to the before and after procedures of any other calls to
dynamic-windthat occur within the dynamic extent of the call to thunk are considered to be within the dynamic extent of the call to thunk.More precisely, an escape procedure transfers control out of the dynamic extent of a set of zero or more active
dynamic-windcalls x ... and transfer control into the dynamic extent of a set of zero or more activedynamic-windcalls y .... It leaves the dynamic extent of the most recent x and calls without arguments the corresponding after procedure. If the after procedure returns, the escape procedure proceeds to the next most recent x, and so on. Once each x has been handled in this manner, the escape procedure calls without arguments the before procedure corresponding to the least recent y. If the before procedure returns, the escape procedure reenters the dynamic extent of the least recent y and proceeds with the next least recent y, and so on. Once each y has been handled in this manner, control is transferred to the continuation packaged in the escape procedure.Implementation responsibilities: The implementation must check the restrictions on thunk and after only if they are actually called.
(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 'talk2) (reverse path)))) ⇒ (connect talk1 disconnect connect talk2 disconnect) (let ((n 0)) (call-with-current-continuation (lambda (k) (dynamic-wind (lambda () (set! n (+ n 1)) (k)) (lambda () (set! n (+ n 2))) (lambda () (set! n (+ n 4)))))) n) ⇒ 1 (let ((n 0)) (call-with-current-continuation (lambda (k) (dynamic-wind values (lambda () (dynamic-wind values (lambda () (set! n (+ n 1)) (k)) (lambda () (set! n (+ n 2)) (k)))) (lambda () (set! n (+ n 4)))))) n) ⇒ 7NOTE Entering a dynamic extent restores its dynamic environment.
“Named
let” is a variant on the syntax ofletthat provides a general looping construct and may also be used to express recursion. It has the same syntax and semantics as ordinaryletexcept that <variable> is bound within <body> to a procedure whose parameters are the bound variables and whose body is <body>. Thus the execution of <body> may be repeated by invoking the procedure named by <variable>.(let loop ((numbers '(3 -2 1 6 -5)) (nonneg '()) (neg '())) (cond ((null? numbers) (list nonneg neg)) ((>= (car numbers) 0) (loop (cdr numbers) (cons (car numbers) nonneg) neg)) ((< (car numbers) 0) (loop (cdr numbers) nonneg (cons (car numbers) neg))))) ⇒ ((6 1 3) (-5 -2))
“Backquote” or “quasiquote” expressions are useful for constructing a list or vector structure when some but not all of the desired structure is known in advance.
<qq template> should be as specified by the grammar at the end of this entry.
If no
unquoteorunquote-splicingforms appear within the <qq template>, the result of evaluating(quasiquote <qq template>)is equivalent to the result of evaluating(quote <qq template>).If an
(unquote <expression> ...)form appears inside a <qq template>, however, the <expression>s are evaluated (“unquoted”) and their results are inserted into the structure instead of theunquoteform.If an
(unquote-splicing <expression> ...)form appears inside a <qq template>, then the <expression>s must evaluate to lists; the opening and closing parentheses of the lists are then “stripped away” and the elements of the lists are inserted in place of theunquote-splicingform.Any
unquote-splicingor multi–operandunquoteform must appear only within a list or vector <qq template>.The following abbreviations may be used:
(quasiquote <qq template>) = `<qq template> (unquote <expression>) = ,<expression> (unquote-splicing <expression>) = ,@<expression>Examples:
`(list ,(+ 1 2) 4) ⇒ (list 3 4) (let ((name 'a)) `(list ,name ',name)) ⇒ (list a (quote a)) `(a ,(+ 1 2) ,@(map abs '(4 -5 6)) b) ⇒ (a 3 4 5 6 b) `((foo ,(- 10 3)) ,@(cdr '(c)) . ,(car '(cons))) ⇒ ((foo 7) . cons) `#(10 5 ,(sqrt 4) ,@(map sqrt '(16 9)) 8) ⇒ #(10 5 2 4 3 8) (let ((name 'foo)) `((unquote name name name))) ⇒ (foo foo foo) (let ((name '(foo))) `((unquote-splicing name name name))) ⇒ (foo foo foo) (let ((q '((append x y) (sqrt 9)))) ``(foo ,,@q)) ⇒ `(foo (unquote (append x y) (sqrt 9))) (let ((x '(2 3)) (y '(4 5))) `(foo (unquote (append x y) (sqrt 9)))) ⇒ (foo (2 3 4 5) 3)Quasiquote forms may be nested. Substitutions are made only for unquoted components appearing at the same nesting level as the outermost
quasiquote. The nesting level increases by one inside each successive quasiquotation, and decreases by one inside each unquotation.`(a `(b ,(+ 1 2) ,(foo ,(+ 1 3) d) e) f) ⇒ (a `(b ,(+ 1 2) ,(foo 4 d) e) f) (let ((name1 'x) (name2 'y)) `(a `(b ,,name1 ,',name2 d) e)) ⇒ (a `(b ,x ,'y d) e)A
quasiquoteexpression may return either fresh, mutable objects or literal structure for any structure that is constructed at run time during the evaluation of the expression. Portions that do not need to be rebuilt are always literal. Thus:(let ((a 3)) `((1 2) ,a ,4 ,'five 6))may be equivalent to either of the following expressions:
'((1 2) 3 4 five 6) (let ((a 3)) (cons '(1 2) (cons a (cons 4 (cons 'five '(6))))))However, it is not equivalent to this expression:
(let ((a 3)) (list (list 1 2) a 4 'five 6))It is a syntax violation if any of the identifiers
quasiquote,unquote, orunquote-splicingappear in positions within a <qq template> other than as described above.The following grammar for quasiquote expressions is not context–free. It is presented as a recipe for generating an infinite number of production rules. Imagine a copy of the following rules for D = 1, 2, 3, .... D keeps track of the nesting depth.
<qq template> -> <qq template 1> <qq template 0> -> <expression> <quasiquotation D> -> (quasiquote <qq template D>) <qq template D> -> <lexeme datum> | <list qq template D> | <vector qq template D> | <unquotation D> <list qq template D> -> (<qq template or splice D>*) | (<qq template or splice D>+ . <qq template D>) | <quasiquotation D+1> <vector qq template D> -> #(<qq template or splice D>*) <unquotation D> -> (unquote <qq template D-1>) <qq template or splice D> -> <qq template D> | <splicing unquotation D> <splicing unquotation D> -> (unquote-splicing <qq template D-1>*) | (unquote <qq template D-1>*)In
quasiquotations, a <list qq template D> can sometimes be confused with either an <unquotation D> or a <splicing unquotation D>. The interpretation as an <unquotation> or <splicing unquotation D> takes precedence.
The let-syntax and letrec-syntax forms bind keywords.
Like a begin form, a let-syntax or letrec-syntax
form may appear in a definition context, in which case it is treated as
a definition, and the forms in the body must also be definitions. A
let-syntax or letrec-syntax form may also appear in an
expression context, in which case the forms within their bodies must be
expressions.
<bindings> must have the form:
((<keyword> <expression>) ...)Each <keyword> is an identifier, and each <expression> is an expression that evaluates, at macro–expansion time, to a transformer. Transformers may be created by
syntax-rulesoridentifier-syntaxor by one of the other mechanisms described in Syntax–case. It is a syntax violation for <keyword> to appear more than once in the list of keywords being bound.The <form>s are expanded in the syntactic environment obtained by extending the syntactic environment of the
let-syntaxform with macros whose keywords are the <keyword>s, bound to the specified transformers. Each binding of a <keyword> has the <form>s as its region.The <form>s of a
let-syntaxform are treated, whether in definition or expression context, as if wrapped in an implicitbegin. Thus definitions in the result of expanding the <form>s have the same region as any definition appearing in place of thelet-syntaxform would have.Implementation responsibilities: The implementation should detect if the value of <expression> cannot possibly be a transformer.
(let-syntax ((when (syntax-rules () ((when test stmt1 stmt2 ...) (if test (begin stmt1 stmt2 ...)))))) (let ((if #t)) (when if (set! if 'now)) if)) ⇒ now (let ((x 'outer)) (let-syntax ((m (syntax-rules () ((m) x)))) (let ((x 'inner)) (m)))) ⇒ outer (let () (let-syntax ((def (syntax-rules () ((def stuff ...) (define stuff ...))))) (def foo 42)) foo) ⇒ 42 (let () (let-syntax ()) 5) ⇒ 5
Same as for
let-syntax.The <form>s are expanded in the syntactic environment obtained by extending the syntactic environment of the
letrec-syntaxform with macros whose keywords are the <keyword>s, bound to the specified transformers. Each binding of a <keyword> has the <bindings> as well as the <form>s within its region, so the transformers can transcribe forms into uses of the macros introduced by theletrec-syntaxform.The <form>s of a
letrec-syntaxform are treated, whether in definition or expression context, as if wrapped in an implicitbegin. Thus definitions in the result of expanding the <form>s have the same region as any definition appearing in place of theletrec-syntaxform would have.Implementation responsibilities: The implementation should detect if the value of <expression> cannot possibly be a transformer.
(letrec-syntax ((my-or (syntax-rules () ((my-or) #f) ((my-or e) e) ((my-or e1 e2 ...) (let ((temp e1)) (if temp temp (my-or e2 ...))))))) (let ((x #f) (y 7) (temp 8) (let odd?) (if even?)) (my-or x (let temp) (if y) y))) ⇒ 7The following example highlights how
let-syntaxandletrec-syntaxdiffer.(let ((f (lambda (x) (+ x 1)))) (let-syntax ((f (syntax-rules () ((f x) x))) (g (syntax-rules () ((g x) (f x))))) (list (f 1) (g 1)))) ⇒ (1 2) (let ((f (lambda (x) (+ x 1)))) (letrec-syntax ((f (syntax-rules () ((f x) x))) (g (syntax-rules () ((g x) (f x))))) (list (f 1) (g 1)))) ⇒ (1 1)The two expressions are identical except that the
let-syntaxform in the first expression is aletrec-syntaxform in the second. In the first expression, the ‘f’ occurring in ‘g’ refers to thelet–bound variable ‘f’, whereas in the second it refers to the keywordfwhose binding is established by theletrec-syntaxform.
Each <literal> must be an identifier. Each <syntax rule> must have the following form:
(<srpattern> <template>)An <srpattern> is a restricted form of <pattern>, namely, a nonempty <pattern> in one of four parenthesized forms below whose first subform is an identifier or an underscore ‘_’. A <pattern> is an identifier, constant, or one of the following.
(<pattern> ...) (<pattern> <pattern> ... . <pattern>) (<pattern> ... <pattern> <ellipsis> <pattern> ...) (<pattern> ... <pattern> <ellipsis> <pattern> ... . <pattern>) #(<pattern> ...) #(<pattern> ... <pattern> <ellipsis> <pattern> ...)An <ellipsis> is the identifier ‘...’ (three periods).
A <template> is a pattern variable, an identifier that is not a pattern variable, a pattern datum, or one of the following.
(<subtemplate> ...) (<subtemplate> ... . <template>) #(<subtemplate> ...)A <subtemplate> is a <template> followed by zero or more ellipses.
An instance of
syntax-rulesevaluates, at macro–expansion time, to a new macro transformer by specifying a sequence of hygienic rewrite rules. A use of a macro whose keyword is associated with a transformer specified bysyntax-rulesis matched against the patterns contained in the <syntax rule>s, beginning with the leftmost <syntax rule>. When a match is found, the macro use is transcribed hygienically according to the template. It is a syntax violation when no match is found.An identifier appearing within a <pattern> may be an underscore ‘_’, an ellipsis ‘...’ or a literal identifier listed in the list of literals
(<literal> ...). All other identifiers appearing within a <pattern> are pattern variables. It is a syntax violation if an ellipsis or underscore appears in(<literal> ...).While the first subform of <srpattern> may be an identifier, the identifier is not involved in the matching and is not considered a pattern variable or literal identifier.
Pattern variables match arbitrary input subforms and are used to refer to elements of the input. It is a syntax violation if the same pattern variable appears more than once in a <pattern>.
Underscores also match arbitrary input subforms but are not pattern variables and so cannot be used to refer to those elements. Multiple underscores may appear in a <pattern>.
A <literal> identifier matches an input subform if and only if the input subform is an identifier and either both its occurrence in the input expression and its occurrence in the list of literals have the same lexical binding, or the two identifiers have the same name and both have no lexical binding.
A subpattern followed by an ellipsis can match zero or more elements of the input.
More formally, an input form F matches a pattern P if and only if one of the following holds:
- P is an underscore (‘_’).
- P is a pattern variable.
- P is a literal identifier and F is an identifier such that both P and F would refer to the same binding if both were to appear in the output of the macro outside of any bindings inserted into the output of the macro. (If neither of two like–named identifiers refers to any binding, i.e., both are undefined, they are considered to refer to the same binding.)
- P is of the form:
(P_1 ... P_n)and F is a list of n elements that match P_1 through P_n.
- P is of the form:
(P_1 ... P_n . P_x)and F is a list or improper list of n or more elements whose first n elements match P_1 through P_n and whose n-th cdr matches P_x.
- P is of the form:
(P_1 ... P_k P_e <ellipsis> P_(m+1) ... P_n)where <ellipsis> is the identifier ‘...’ and F is a list of n elements whose first k elements match P_1 through P_k, whose next m-k elements each match P_e, and whose remaining n-m elements match P_(m+1) through P_n.
- P is of the form:
(P_1 ... P_k P_e <ellipsis> P_(m+1) ... P_n . P_x)where <ellipsis> is the identifier ‘...’ and F is a list or improper list of n elements whose first k elements match P_1 through P_k, whose next m-k elements each match P_e, whose next n-m elements match P_(m+1) through P_n, and whose nth and final cdr matches P_x.
- P is of the form:
#(P_1 ... P_n)and F is a vector of n elements that match P_1 through P_n.
- P is of the form:
#(P_1 ... P_k P_e <ellipsis> P_(m+1) ... P_n)where <ellipsis> is the identifier ‘...’ and F is a vector of n or more elements whose first k elements match P_1 through P_k, whose next m-k elements each match P_e, and whose remaining n-m elements match P_(m+1) through P_n.
- P is a pattern datum (any nonlist, nonvector, nonsymbol datum) and F is equal to P in the sense of the
equal?procedure.When a macro use is transcribed according to the template of the matching <syntax rule>, pattern variables that occur in the template are replaced by the subforms they match in the input.
Pattern data and identifiers that are not pattern variables or ellipses are copied into the output. A subtemplate followed by an ellipsis expands into zero or more occurrences of the subtemplate. Pattern variables that occur in subpatterns followed by one or more ellipses may occur only in subtemplates that are followed by (at least) as many ellipses. These pattern variables are replaced in the output by the input subforms to which they are bound, distributed as specified. If a pattern variable is followed by more ellipses in the subtemplate than in the associated subpattern, the input form is replicated as necessary. The subtemplate must contain at least one pattern variable from a subpattern followed by an ellipsis, and for at least one such pattern variable, the subtemplate must be followed by exactly as many ellipses as the subpattern in which the pattern variable appears. (Otherwise, the expander would not be able to determine how many times the subform should be repeated in the output.) It is a syntax violation if the constraints of this paragraph are not met.
A template of the form
(<ellipsis> <template>)is identical to <template>, except that ellipses within the template have no special meaning. That is, any ellipses contained within <template> are treated as ordinary identifiers. In particular, the template(... ...)produces a single ellipsis, ‘...’. This allows syntactic abstractions to expand into forms containing ellipses.(define-syntax be-like-begin (syntax-rules () ((be-like-begin name) (define-syntax name (syntax-rules () ((name expr (... ...)) (begin expr (... ...)))))))) (be-like-begin sequence) (sequence 1 2 3 4) ⇒ 4As an example for hygienic use of auxiliary identifier, if
letandcondare defined as in let and cond then they are hygienic (as required) and the following is not an error.(let ((=> #f)) (cond (#t => 'ok))) ⇒ okThe macro transformer for
condrecognizes ‘=>’ as a local variable, and hence an expression, and not as the identifier ‘=>’, which the macro transformer treats as a syntactic keyword. Thus the example expands into:(let ((=> #f)) (if #t (begin => 'ok)))instead of:
(let ((=> #f)) (let ((temp #t)) (if temp ('ok temp))))which would result in an assertion violation.
The <id>s must be identifiers. The <template>s must be as for
syntax-rules.When a keyword is bound to a transformer produced by the first form of
identifier-syntax, references to the keyword within the scope of the binding are replaced by <template>.(define p (cons 4 5)) (define-syntax p.car (identifier-syntax (car p))) p.car ⇒ 4 (set! p.car 15) error--> exception &syntaxThe second, more general, form of
identifier-syntaxpermits the transformer to determine what happens when ‘set!’ is used. In this case, uses of the identifier by itself are replaced by <template1>, and uses of ‘set!’ with the identifier are replaced by <template2>.(define p (cons 4 5)) (define-syntax p.car (identifier-syntax (_ (car p)) ((set! _ e) (set-car! p e)))) (set! p.car 15) p.car ⇒ 15 p ⇒ (15 . 5)
A tail call is a procedure call that occurs in a tail context. Tail contexts are defined inductively. Note that a tail context is always determined with respect to a particular lambda expression.
(lambda <formals>
<definition>*
<expression>* <tail expression>)
(if <expression> <tail expression> <tail expression>)
(if <expression> <tail expression>)
(cond <cond clause>+)
(cond <cond clause>* (else <tail sequence>))
(case <expression>
<case clause>+)
(case <expression>
<case clause>*
(else <tail sequence>))
(and <expression>* <tail expression>)
(or <expression>* <tail expression>)
(let <bindings> <tail body>)
(let <variable> <bindings> <tail body>)
(let* <bindings> <tail body>)
(letrec* <bindings> <tail body>)
(letrec <bindings> <tail body>)
(let-values <mv-bindings> <tail body>)
(let*-values <mv-bindings> <tail body>)
(let-syntax <bindings> <tail body>)
(letrec-syntax <bindings> <tail body>)
(begin <tail sequence>)
A <cond clause> is:
(<test> <tail sequence>)
a <case clause> is:
((<datum>*) <tail sequence>)
a <tail body> is:
<definition>* <tail sequence>
and a <tail sequence> is:
<expression>* <tail expression>
cond expression is in a tail context, and has a clause of
the form (<expression1> => <expression2>) then the
(implied) call to the procedure that results from the evaluation of
<expression2> is in a tail context. <expression2> itself
is not in a tail context.
Certain built–in procedures must also perform tail calls. The first
argument passed to apply and to call/cc, and the second
argument passed to call-with-values, must be called via a tail
call.
In the following example the only tail call is the call to f.
None of the calls to g or h are tail calls. The reference
to x is in a tail context, but it is not a call and thus is not a
tail call.
(lambda ()
(if (g)
(let ((x (h)))
x)
(and (g) (f))))
NOTE Implementations may recognize that some non–tail calls, such as the call tohabove, can be evaluated as though they were tail calls. In the example above, theletexpression could be compiled as a tail call toh. (The possibility ofhreturning an unexpected number of values can be ignored, because in that case the effect of theletis explicitly unspecified and implementation–dependent.)
The procedures exported by the (rnrs unicode (6)) library provide
access to some aspects of the Unicode semantics for characters and
strings: category information, case–independent comparisons, case
mappings, and normalization.
Some of the procedures that operate on characters or strings ignore the
difference between upper case and lower case. These procedures have
-ci (for “case insensitive”) embedded in their names.
These procedures take a character argument and return a character result.
If the argument is an upper–case or title–case character, and if there is a single character that is its lower–case form, then
char-downcasereturns that character.If the argument is a lower–case or title–case character, and there is a single character that is its upper–case form, then
char-upcasereturns that character.If the argument is a lower–case or upper–case character, and there is a single character that is its title–case form, then
char-titlecasereturns that character.If the argument is not a title–case character and there is no single character that is its title–case form, then
char-titlecasereturns the upper–case form of the argument.Finally, if the character has a case–folded character, then
char-foldcasereturns that character. Otherwise the character returned is the same as the argument.For Turkic characters
#\x130and#\x131,char-foldcasebehaves as the identity function; otherwisechar-foldcaseis the same aschar-downcasecomposed withchar-upcase.(char-upcase #\i) ⇒ #\I (char-downcase #\i) ⇒ #\i (char-titlecase #\i) ⇒ #\I (char-foldcase #\i) ⇒ #\iNOTEchar-titlecasedoes not always return a title–case character.NOTE These procedures are consistent with Unicode's locale–independent mappings from scalar values to scalar values for upcase, downcase, titlecase, and case–folding operations. These mappings can be extracted from UnicodeData.txt and CaseFolding.txt from the Unicode Consortium, ignoring Turkic mappings in the latter.Note that these character–based procedures are an incomplete approximation to case conversion, even ignoring the user's locale. In general, case mappings require the context of a string, both in arguments and in result. The
string-upcase,string-downcase,string-titlecase, andstring-foldcaseprocedures (stdlib unicode strings perform more general case conversion.
These procedures are similar to
char=?, etc., but operate on the case–folded versions of the characters.(char-ci<? #\z #\Z) ⇒ #f (char-ci=? #\z #\Z) ⇒ #f
These procedures return
#tif their arguments are alphabetic, numeric, whitespace, upper–case, lower–case, or title–case characters, respectively; otherwise they return#f.A character is alphabetic if it has the Unicode “Alphabetic” property. A character is numeric if it has the Unicode “Numeric” property. A character is whitespace if has the Unicode “White_Space” property. A character is upper case if it has the Unicode “Uppercase” property, lower case if it has the “Lowercase” property, and title case if it is in the Lt general category.
(char-alphabetic? #\a) ⇒ #t (char-numeric? #\1) ⇒ #t (char-whitespace? #\space) ⇒ #t (char-whitespace? #\x00A0) ⇒ #t (char-lower-case? #\x00AA) ⇒ #t (char-title-case? #\I) ⇒ #f (char-title-case? #\x01C5) ⇒ #t
Return a symbol representing the Unicode general category of char, one of
Lu,Ll,Lt,Lm,Lo,Mn,Mc,Me,Nd,Nl,No,Ps,Pe,Pi,Pf,Pd,Pc,Po,Sc,Sm,Sk,So,Zs,Zp,Zl,Cc,Cf,Cs,Co, orCn.(char-general-category #\a) ⇒ Ll (char-general-category #\space) ⇒ Zs (char-general-category #\x10FFFF) ⇒ Cn
These procedures take a string argument and return a string result. They are defined in terms of Unicode's locale–independent case mappings from Unicode scalar–value sequences to scalar–value sequences. In particular, the length of the result string can be different from the length of the input string. When the specified result is equal in the sense of
string=?to the argument, these procedures may return the argument instead of a newly allocated string.The
string-upcaseprocedure converts a string to upper case;string-downcaseconverts a string to lower case. Thestring-foldcaseprocedure converts the string to its case–folded counterpart, using the full case–folding mapping, but without the special mappings for Turkic languages. Thestring-titlecaseprocedure converts the first cased character of each word, and downcases all other cased characters.(string-upcase "Hi") ⇒ "HI" (string-downcase "Hi") ⇒ "hi" (string-foldcase "Hi") ⇒ "hi" (string-titlecase "kNock KNoCK") ⇒ "Knock Knock" (string-titlecase "who's there?") ⇒ "Who's There?" (string-titlecase "r6rs") ⇒ "R6rs" (string-titlecase "R6RS") ⇒ "R6rs"NOTE The case mappings needed for implementing these procedures can be extracted from UnicodeData.txt, SpecialCasing.txt, WordBreakProperty.txt (the “MidLetter” property partly defines case–ignorable characters), and CaseFolding.txt from the Unicode Consortium.Since these procedures are locale–independent, they may not be appropriate for some locales.
NOTE Word breaking, as needed for the correct casing of the upper case greek sigma and forstring-titlecase, is specified in Unicode Standard Annex #29.
These procedures are similar to
string=?, etc., but operate on the case–folded versions of the strings.(string-ci<? "z" "Z") ⇒ #f (string-ci=? "z" "Z") ⇒ #t
These procedures take a string argument and return a string result, which is the input string normalized to Unicode normalization form D, KD, C, or KC, respectively. When the specified result is equal in the sense of
string=?to the argument, these procedures may return the argument instead of a newly allocated string.(string-normalize-nfd "\xE9;") ⇒ "\x65;\x301;" (string-normalize-nfc "\xE9;") ⇒ "\xE9;" (string-normalize-nfd "\x65;\x301;") ⇒ "\x65;\x301;" (string-normalize-nfc "\x65;\x301;") ⇒ "\xE9;"
Many applications deal with blocks of binary data by accessing them in
various ways—extracting signed or unsigned numbers of various sizes.
Therefore, the (rnrs bytevectors (6)) library provides a single type
for blocks of binary data with multiple ways to access that data. It
deals with integers and floating–point representations in various sizes
with specified endianness.
Bytevectors are objects of a disjoint type. Conceptually, a bytevector represents a sequence of 8-bit bytes. The description of bytevectors uses the term byte for an exact integer object in the interval (-128, ..., 127) and the term octet for an exact integer object in the interval (0, ..., 255). A byte corresponds to its two's complement representation as an octet.
The length of a bytevector is the number of bytes it contains. This number is fixed. A valid index into a bytevector is an exact, non–negative integer object less than the length of the bytevector. The first byte of a bytevector has index 0; the last byte has an index one less than the length of the bytevector.
Generally, the access procedures come in different flavors according to the size of the represented integer and the endianness of the representation. The procedures also distinguish signed and unsigned representations. The signed representations all use two's complement.
Like string literals, literals representing bytevectors do not need to be quoted:
#vu8(12 23 123) ⇒ #vu8(12 23 123)
Many operations described in this chapter accept an endianness argument. Endianness describes the encoding of exact integer objects as several contiguous bytes in a bytevector. For this purpose, the binary representation of the integer object is split into consecutive bytes. The little–endian encoding places the least significant byte of an integer first, with the other bytes following in increasing order of significance. The big–endian encoding places the most significant byte of an integer first, with the other bytes following in decreasing order of significance.
This terminology also applies to IEEE 754 numbers: IEEE 754 describes how to represent a floating–point number as an exact integer object, and endianness describes how the bytes of such an integer are laid out in a bytevector.
NOTE Little– and big–endianness are only the most common kinds of endianness. Some architectures distinguish between the endianness at different levels of a binary representation.
The name of <endianness symbol> must be a symbol describing an endianness. An implementation must support at least the symbols
bigandlittle, but may support other endianness symbols.
(endianness <endianness symbol>)evaluates to the symbol named <endianness symbol>. Whenever one of the procedures operating on bytevectors accepts an endianness as an argument, that argument must be one of these symbols. It is a syntax violation for <endianness symbol> to be anything other than an endianness symbol supported by the implementation.NOTE Implementors should use widely accepted designations for endianness symbols other thanbigandlittle.NOTE Only the name of <endianness symbol> is significant.
Return the endianness symbol associated implementation's preferred endianness (usually that of the underlying machine architecture). This may be any <endianness symbol>, including a symbol other than
bigandlittle.
Return a newly allocated bytevector of k bytes. If the fill argument is missing, the initial contents of the returned bytevector are unspecified. If the fill argument is present, it must be an exact integer object in the interval (-128, ... 255) that specifies the initial value for the bytes of the bytevector: If fill is positive, it is interpreted as an octet; if it is negative, it is interpreted as a byte.
Return, as an exact integer object, the number of bytes in bytevector.
Return
#tif bytevector1 and bytevector2 are equal; that is, if they have the same length and equal bytes at all valid indices. It returns#fotherwise.
The fill argument is as in the description of the
make-bytevectorprocedure. Thebytevector-fill!procedure stores fill in every element of bytevector and returns unspecified values. Analogous tovector-fill!.
source and target must be bytevectors. source-start, target-start, and k must be non–negative exact integer objects that satisfy:
0 <= source-start <= source-start + k <= l_source 0 <= target-start <= target-start + k <= l_targetwhere l_source is the length of source and l_target is the length of target.
The
bytevector-copy!procedure copies the bytes from source at indices:source-start, ..., source-start + k - 1to consecutive indices in target starting at target-index.
This must work even if the memory regions for the source and the target overlap, i.e., the bytes at the target location after the copy must be equal to the bytes at the source location before the copy.
This returns unspecified values.
(let ((b (u8-list->bytevector '(1 2 3 4 5 6 7 8)))) (bytevector-copy! b 0 b 3 4) (bytevector->u8-list b)) ⇒ (1 2 3 1 2 3 4 8)
k must be a valid index of bytevector.
The
bytevector-u8-refprocedure returns the byte at index k of bytevector, as an octet.The
bytevector-s8-refprocedure returns the byte at index k of bytevector, as a (signed) byte.(let ((b1 (make-bytevector 16 -127)) (b2 (make-bytevector 16 255))) (list (bytevector-s8-ref b1 0) (bytevector-u8-ref b1 0) (bytevector-s8-ref b2 0) (bytevector-u8-ref b2 0))) ⇒ (-127 129 -1 255)
k must be a valid index of bytevector.
The
bytevector-u8-set!procedure stores octet in element k of bytevector.The
bytevector-s8-set!procedure stores the two's–complement representation of byte in element k of bytevector.Both procedures return unspecified values.
(let ((b (make-bytevector 16 -127))) (bytevector-s8-set! b 0 -126) (bytevector-u8-set! b 1 246) (list (bytevector-s8-ref b 0) (bytevector-u8-ref b 0) (bytevector-s8-ref b 1) (bytevector-u8-ref b 1))) ⇒ (-126 130 -10 246)
list must be a list of octets.
The
bytevector->u8-listprocedure returns a newly allocated list of the octets of bytevector in the same order.The
u8-list->bytevectorprocedure returns a newly allocated bytevector whose elements are the elements of list list, in the same order. It is analogous tolist->vector.
size must be a positive exact integer object. k, ..., k+size-1 must be valid indices of bytevector.
The
bytevector-uint-refprocedure retrieves the exact integer object corresponding to the unsigned representation of size size and specified by endianness at indices k, ..., k + size - 1.The
bytevector-sint-refprocedure retrieves the exact integer object corresponding to the two's–complement representation of size size and specified by endianness at indices k, ..., k+size-1.For
bytevector-uint-set!, n must be an exact integer object in the interval(0, ..., 256^(size-1)).The
bytevector-uint-set!procedure stores the unsigned representation of size size and specified by endianness into bytevector at indices k, ..., k + size - 1.For
bytevector-sint-set!, n must be an exact integer object in the interval (-(256^size)/2, ..., (256^(size))/2-1).bytevector-sint-set!stores the two's–complement representation of size size and specified by endianness into bytevector at indices k, ..., k + size - 1.The
...-set!procedures return unspecified values.(define b (make-bytevector 16 -127)) (bytevector-uint-set! b 0 (- (expt 2 128) 3) (endianness little) 16) (bytevector-uint-ref b 0 (endianness little) 16) ⇒ #xfffffffffffffffffffffffffffffffd (bytevector-sint-ref b 0 (endianness little) 16) ⇒ -3 (bytevector->u8-list b) ⇒ (253 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255) (bytevector-uint-set! b 0 (- (expt 2 128) 3) (endianness big) 16) (bytevector-uint-ref b 0 (endianness big) 16) ⇒ #xfffffffffffffffffffffffffffffffd (bytevector-sint-ref b 0 (endianness big) 16) ⇒ -3 (bytevector->u8-list b) ⇒ (255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 253))
size must be a positive exact integer object.
For
uint-list->bytevector, list must be a list of exact integer objects in the interval (0, ..., (256^size)-1).For
sint-list->bytevector, list must be a list of exact integer objects in the interval (-(256^size)/2, ..., (256^(size))/2-1).The length of bytevector must be divisible by size.
These procedures convert between lists of integer objects and their consecutive representations according to size and endianness in the bytevector objects in the same way as
bytevector->u8-listandu8-list->bytevectordo for one–byte representations.(let ((b (u8-list->bytevector '(1 2 3 255 1 2 1 2)))) (bytevector->sint-list b (endianness little) 2)) ⇒ (513 -253 513 513) (let ((b (u8-list->bytevector '(1 2 3 255 1 2 1 2)))) (bytevector->uint-list b (endianness little) 2)) ⇒ (513 65283 513 513)
k must be a valid index of bytevector; so must k+1.
For
bytevector-u16-set!andbytevector-u16-native-set!, n must be an exact integer object in the interval 0 <= n <= 2^16-1.For
bytevector-s16-set!andbytevector-s16-native-set!, n must be an exact integer object in the interval -2^15 <= n <= 2^15-1.These procedures retrieve and set two–byte representations of numbers at indices k and k+1 according to the endianness specified by endianness. The procedures with
u16in their names deal with the unsigned representation; those withs16in their names deal with the two's–complement representation.The procedures with
nativein their names employ the native endianness, and work only at aligned indices: k must be a multiple of 2.The
...-set!procedures return unspecified values.(define b (u8-list->bytevector '(255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 253))) (bytevector-u16-ref b 14 (endianness little)) ⇒ 65023 (bytevector-s16-ref b 14 (endianness little)) ⇒ -513 (bytevector-u16-ref b 14 (endianness big)) ⇒ 65533 (bytevector-s16-ref b 14 (endianness big)) ⇒ -3 (bytevector-u16-set! b 0 12345 (endianness little)) (bytevector-u16-ref b 0 (endianness little)) ⇒ 12345 (bytevector-u16-native-set! b 0 12345) (bytevector-u16-native-ref b 0) ⇒ 12345 (bytevector-u16-ref b 0 (endianness little)) ⇒ unspecified
k, ..., k+3 must be valid indices of bytevector.
For
bytevector-u32-set!andbytevector-u32-native-set!, n must be an exact integer object in the interval 0 <= n <= 2^32-1.For
bytevector-s32-set!andbytevector-s32-native-set!, n must be an exact integer object in the interval -2^31 <= n <= 2^32-1.These retrieve and set four–byte representations of numbers at indices k, ..., k+3, according to the endianness specified by endianness. The procedures with
u32in their names deal with the unsigned representation; those withs32with the two's–complement representation.The procedures with
nativein their names employ the native endianness, and work only at aligned indices: k must be a multiple of 4.The
...-set!procedures return unspecified values.(define b (u8-list->bytevector '(255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 253))) (bytevector-u32-ref b 12 (endianness little)) ⇒ 4261412863 (bytevector-s32-ref b 12 (endianness little)) ⇒ -33554433 (bytevector-u32-ref b 12 (endianness big)) ⇒ 4294967293 (bytevector-s32-ref b 12 (endianness big)) ⇒ -3
k, ..., k+7 must be valid indices of bytevector.
For
bytevector-u64-set!andbytevector-u64-native-set!, n must be an exact integer object in the interval 0 <= n <= 2^64-1.For
bytevector-s64-set!andbytevector-s64-native-set!, n must be an exact integer object in the interval -2^63 <= n <= 2^64-1.These retrieve and set eight–byte representations of numbers at indices k, ..., k+7, according to the endianness specified by endianness. The procedures with
u64in their names deal with the unsigned representation; those withs64with the two's–complement representation.The procedures with
nativein their names employ the native endianness, and work only at aligned indices: k must be a multiple of 8.The
...-set!procedures return unspecified values.(define b (u8-list->bytevector '(255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 253))) (bytevector-u64-ref b 8 (endianness little)) ⇒ 18302628885633695743 (bytevector-s64-ref b 8 (endianness little)) ⇒ -144115188075855873 (bytevector-u64-ref b 8 (endianness big)) ⇒ 18446744073709551613 (bytevector-s64-ref b 8 (endianness big)) ⇒ -3
k, ..., k+3 must be valid indices of bytevector.
For
bytevector-ieee-single-native-ref, k must be a multiple of 4.These procedures return the inexact real number object that best represents the IEEE 754 single–precision number represented by the four bytes beginning at index k.
k, ..., k+7 must be valid indices of bytevector.
For
bytevector-ieee-double-native-ref, k must be a multiple of 8.These procedures return the inexact real number object that best represents the IEEE 754 double–precision number represented by the eight bytes beginning at index k.
k, ..., k+3 must be valid indices of bytevector.
For
bytevector-ieee-single-native-set!, k must be a multiple of 4.These procedures store an IEEE 754 single–precision representation of x into elements k through k+3 of bytevector, and return unspecified values.
k, ..., k+7 must be valid indices of bytevector.
For
bytevector-ieee-double-native-set!, k must be a multiple of 8.These procedures store an IEEE 754 double–precision representation of x into elements k through k+7 of bytevector, and return unspecified values.
This section describes procedures that convert between strings and
bytevectors containing Unicode encodings of those strings. When
decoding bytevectors, encoding errors are handled as with the
replace semantics of textual I/O: If an invalid or incomplete
character encoding is encountered, then the replacement character
U+FFFD is appended to the string being generated, an appropriate
number of bytes are ignored, and decoding continues with the following
bytes.
Return a newly allocated (unless empty) bytevector that contains the UTF-8 encoding of the given string.
If endianness is specified, it must be the symbol
bigor the symbollittle. Thestring->utf16procedure returns a newly allocated (unless empty) bytevector that contains the UTF-16BE or UTF-16LE encoding of the given string (with no byte–order mark). If endianness is not specified or isbig, then UTF-16BE is used. If endianness islittle, then UTF-16LE is used.
If endianness is specified, it must be the symbol
bigor the symbollittle. Thestring->utf32procedure returns a newly allocated (unless empty) bytevector that contains the UTF-32BE or UTF-32LE encoding of the given string (with no byte mark). If endianness is not specified or isbig, then UTF-32BE is used. If endianness islittle, then UTF-32LE is used.
Return a newly allocated (unless empty) string whose character sequence is encoded by the given bytevector.
endianness must be the symbol
bigor the symbollittle.The
utf16->stringprocedure returns a newly allocated (unless empty) string whose character sequence is encoded by the given bytevector.bytevector is decoded according to UTF-16, UTF-16BE, UTF-16LE, or a fourth encoding scheme that differs from all three of those as follows: If endianness-mandatory is absent or
#f,utf16->stringdetermines the endianness according to a UTF-16 BOM at the beginning of bytevector if a BOM is present; in this case, the BOM is not decoded as a character. Also in this case, if no UTF-16 BOM is present, endianness specifies the endianness of the encoding. If endianness-mandatory is a true value, endianness specifies the endianness of the encoding, and any UTF-16 BOM in the encoding is decoded as a regular character.NOTE A UTF-16 BOM is either a sequence of bytes#xFE,#xFFspecifyingbigand UTF-16BE, or#xFF,#xFEspecifyinglittleand UTF-16LE.
endianness must be the symbol
bigor the symbollittle.The
utf32->stringprocedure returns a newly allocated (unless empty) string whose character sequence is encoded by the given bytevector.bytevector is decoded according to UTF-32, UTF-32BE, UTF-32LE, or a fourth encoding scheme that differs from all three of those as follows: If endianness-mandatory is absent or
#f,utf32->stringdetermines the endianness according to a UTF-32 BOM at the beginning of bytevector if a BOM is present; in this case, the BOM is not decoded as a character. Also in this case, if no UTF-32 BOM is present, endianness specifies the endianness of the encoding. If endianness-mandatory is a true value, endianness specifies the endianness of the encoding, and any UTF-32 BOM in the encoding is decoded as a regular character.NOTE A UTF-32 BOM is either a sequence of bytes#x00,#x00,#xFE,#xFFspecifyingbigand UTF-32BE, or#xFF,#xFE,#x00,#x00, specifyinglittleand UTF-32LE.
This chapter describes the (rnrs lists (6)) library, which contains
various useful procedures that operate on lists.
proc should accept one argument and return a single value. proc should not mutate list. The
findprocedure applies proc to the elements of list in order. If proc returns a true value for an element,findimmediately returns that element. If proc returns#ffor all elements of the list,findreturns#f. proc is always called in the same dynamic environment asfinditself.(find even? '(3 1 4 1 5 9)) ⇒ 4 (find even? '(3 1 5 1 5 9)) ⇒ #fImplementation responsibilities: The implementation must check that list is a chain of pairs up to the found element, or that it is indeed a list if no element is found. It should not check that it is a chain of pairs beyond the found element. The implementation must check the restrictions on proc to the extent performed by applying it as described. An implementation may check whether proc is an appropriate argument before applying it.
The lists should all have the same length, and proc should accept n arguments and return a single value. proc should not mutate the list arguments.
For natural numbers i = 0, 1, ..., the
for-allprocedure successively applies proc to arguments x_i^1 ... x_i^n, where x_i^j is the i-th element of listj, until#fis returned.If proc returns true values for all but the last element of list1,
for-allperforms a tail call of proc on the k-th elements, where k is the length of list1. If proc returns#fon any set of elements,for-allreturns#fafter the first such application of proc. If the lists are all empty,for-allreturns#t.For natural numbers i = 0, 1, ..., the
existsprocedure applies proc successively to arguments x_i^1 ... x_i^n, where x_i^j is the i-th element of listj, until a true value is returned.If proc returns
#ffor all but the last elements of the lists,existsperforms a tail call of proc on the kth elements, where k is the length of list1. If proc returns a true value on any set of elements,existsreturns that value after the first such application of proc. If the lists are all empty,existsreturns#f.proc is always called in the same dynamic environment as
for-allor, respectively,existsitself.(for-all even? '(3 1 4 1 5 9)) ⇒ #f (for-all even? '(3 1 4 1 5 9 . 2)) ⇒ #f (for-all even? '(2 4 14)) ⇒ #t (for-all even? '(2 4 14 . 9)) error--> exception &assertion (for-all (lambda (n) (and (even? n) n)) '(2 4 14)) ⇒ 14 (for-all < '(1 2 3) '(2 3 4)) ⇒ #t (for-all < '(1 2 4) '(2 3 4)) ⇒ #f (exists even? '(3 1 4 1 5 9)) ⇒ #t (exists even? '(3 1 1 5 9)) ⇒ #f (exists even? '(3 1 1 5 9 . 2)) error--> exception &assertion (exists (lambda (n) (and (even? n) n)) '(2 1 4 14)) ⇒ 2 (exists < '(1 2 4) '(2 3 4)) ⇒ #t (exists > '(1 2 3) '(2 3 4)) ⇒ #fImplementation responsibilities: The implementation must check that the lists are chains of pairs to the extent necessary to determine the return value. If this requires traversing the lists entirely, the implementation should check that the lists all have the same length. If not, it should not check that the lists are chains of pairs beyond the traversal. The implementation must check the restrictions on proc to the extent performed by applying it as described. An implementation may check whether proc is an appropriate argument before applying it.
proc should accept one argument and return a single value. proc should not mutate list.
The
filterprocedure applies proc to each element of list and returns a list of the elements of list for which proc returned a true value.The
partitionprocedure also applies proc to each element of list, but returns two values, the first one a list of the elements of list for which proc returned a true value, and the second a list of the elements of list for which proc returned#f.In both cases, the elements of the result list(s) are in the same order as they appear in the input list. proc is always called in the same dynamic environment as
filteror, respectively,partitionitself. If multiple returns occur fromfilterorpartitions, the return values returned by earlier returns are not mutated.(filter even? '(3 1 4 1 5 9 2 6)) ⇒ (4 2 6) (partition even? '(3 1 4 1 5 9 2 6)) ⇒ (4 2 6) (3 1 1 5 9) ; two valuesImplementation responsibilities: The implementation must check the restrictions on proc to the extent performed by applying it as described. An implementation may check whether proc is an appropriate argument before applying it.
The lists should all have the same length. combine must be a procedure; it should accept one more argument than there are lists and return a single value; it should not mutate the list arguments.
The
fold-leftprocedure iterates the combine procedure over an accumulator value and the elements of the lists from left to right, starting with an accumulator value of nil.More specifically,
fold-leftreturns nil if the lists are empty. If they are not empty, combine is first applied to nil and the respective first elements of the lists in order. The result becomes the new accumulator value, and combine is applied to the new accumulator value and the respective next elements of the list. This step is repeated until the end of the list is reached; then the accumulator value is returned.combine is always called in the same dynamic environment as
fold-leftitself.(fold-left + 0 '(1 2 3 4 5)) ⇒ 15 (fold-left (lambda (a e) (cons e a)) '() '(1 2 3 4 5)) ⇒ (5 4 3 2 1) (fold-left (lambda (count x) (if (odd? x) (+ count 1) count)) 0 '(3 1 4 1 5 9 2 6 5 3)) ⇒ 7 (fold-left (lambda (max-len s) (max max-len (string-length s))) 0 '("longest" "long" "longer")) ⇒ 7 (fold-left cons '(q) '(a b c)) ⇒ ((((q) . a) . b) . c) (fold-left + 0 '(1 2 3) '(4 5 6)) ⇒ 21Implementation responsibilities: The implementation should check that the lists all have the same length. The implementation must check the restrictions on combine to the extent performed by applying it as described. An implementation may check whether combine is an appropriate argument before applying it.
The lists should all have the same length. combine must be a procedure; it should accept one more argument than there are lists and return a single value; combine should not mutate the list arguments.
The
fold-rightprocedure iterates the combine procedure over the elements of the lists from right to left and an accumulator value, starting with an accumulator value of nil.More specifically,
fold-rightreturns nil if the lists are empty. If they are not empty, combine is first applied to the respective last elements of the lists in order and nil. The result becomes the new accumulator value, and combine is applied to the respective previous elements of the lists and the new accumulator value. This step is repeated until the beginning of the list is reached; then the accumulator value is returned.proc is always called in the same dynamic environment as
fold-rightitself.(fold-right + 0 '(1 2 3 4 5)) ⇒ 15 (fold-right cons '() '(1 2 3 4 5)) ⇒ (1 2 3 4 5) (fold-right (lambda (x l) (if (odd? x) (cons x l) l)) '() '(3 1 4 1 5 9 2 6 5)) ⇒ (3 1 1 5 9 5) (fold-right cons '(q) '(a b c)) ⇒ (a b c q) (fold-right + 0 '(1 2 3) '(4 5 6)) ⇒ 21Implementation responsibilities: The implementation should check that the lists all have the same length. The implementation must check the restrictions on combine to the extent performed by applying it as described. An implementation may check whether combine is an appropriate argument before applying it.
proc should accept one argument and return a single value. proc should not mutate list.
Each of these procedures returns a list of the elements of list that do not satisfy a given condition.
The
rempprocedure applies proc to each element of list and returns a list of the elements of list for which proc returned#f. proc is always called in the same dynamic environment asrempitself.The
remove,remv, andremqprocedures return a list of the elements that are not obj. Theremqprocedure useseq?to compare obj with the elements of list, whileremvuseseqv?andremoveusesequal?.The elements of the result list are in the same order as they appear in the input list. If multiple returns occur from
remp, the return values returned by earlier returns are not mutated.(remp even? '(3 1 4 1 5 9 2 6 5)) ⇒ (3 1 1 5 9 5) (remove 1 '(3 1 4 1 5 9 2 6 5)) ⇒ (3 4 5 9 2 6 5) (remv 1 '(3 1 4 1 5 9 2 6 5)) ⇒ (3 4 5 9 2 6 5) (remq 'foo '(bar foo baz)) ⇒ (bar baz)Implementation responsibilities: The implementation must check the restrictions on proc to the extent performed by applying it as described. An implementation may check whether proc is an appropriate argument before applying it.
proc should accept one argument and return a single value. proc should not mutate list.
These procedures return the first sublist of list whose car satisfies a given condition, where the sublists of lists are the lists returned by
(list-taillist k)for k less than the length of list.The
mempprocedure applies proc to the cars of the sublists of list until it finds one for which proc returns a true value. proc is always called in the same dynamic environment asmempitself.The
member,memv, andmemqprocedures look for the first occurrence of obj. If list does not contain an element satisfying the condition, then#f(not the empty list) is returned. Thememberprocedure usesequal?to compare obj with the elements of list, whilememvuseseqv?andmemquseseq?.(memp even? '(3 1 4 1 5 9 2 6 5)) ⇒ (4 1 5 9 2 6 5) (memq 'a '(a b c)) ⇒ (a b c) (memq 'b '(a b c)) ⇒ (b c) (memq 'a '(b c d)) ⇒ #f (memq (list 'a) '(b (a) c)) ⇒ #f (member (list 'a) '(b (a) c)) ⇒ ((a) c) (memq 101 '(100 101 102)) ⇒ unspecified (memv 101 '(100 101 102)) ⇒ (101 102)Implementation responsibilities: The implementation must check that list is a chain of pairs up to the found element, or that it is indeed a list if no element is found. It should not check that it is a chain of pairs beyond the found element. The implementation must check the restrictions on proc to the extent performed by applying it as described. An implementation may check whether proc is an appropriate argument before applying it.
alist (for “association list”) should be a list of pairs. proc should accept one argument and return a single value. Proc should not mutate alist.
These procedures find the first pair in alist whose car field satisfies a given condition, and returns that pair without traversing alist further. If no pair in alist satisfies the condition, then
#fis returned.The
asspprocedure successively applies proc to the car fields of alist and looks for a pair for which it returns a true value. proc is always called in the same dynamic environment asasspitself.The
assoc,assv, andassqprocedures look for a pair that has obj as its car. Theassocprocedure usesequal?to compare obj with the car fields of the pairs in alist, whileassvuseseqv?andassquseseq?.Implementation responsibilities: The implementation must check that alist is a chain of pairs containing pairs up to the found pair, or that it is indeed a list of pairs if no element is found. It should not check that it is a chain of pairs beyond the found element. The implementation must check the restrictions on proc to the extent performed by applying it as described. An implementation may check whether proc is an appropriate argument before applying it.
(define d '((3 a) (1 b) (4 c))) (assp even? d) ⇒ (4 c) (assp odd? d) ⇒ (3 a) (define e '((a 1) (b 2) (c 3))) (assq 'a e) ⇒ (a 1) (assq 'b e) ⇒ (b 2) (assq 'd e) ⇒ #f (assq (list 'a) '(((a)) ((b)) ((c)))) ⇒ #f (assoc (list 'a) '(((a)) ((b)) ((c)))) ⇒ ((a)) (assq 5 '((2 3) (5 7) (11 13))) ⇒ unspecified (assv 5 '((2 3) (5 7) (11 13))) ⇒ (5 7)
If called with at least two arguments,
cons*returns a freshly allocated chain of pairs whose cars are obj1, ..., objn, and whose last cdr is obj. If called with only one argument,cons*returns that argument.(cons* 1 2 '(3 4 5)) ⇒ (1 2 3 4 5) (cons* 1 2 3) ⇒ (1 2 . 3) (cons* 1) ⇒ 1
This chapter describes the (rnrs sorting (6)) library for sorting
lists and vectors.
proc should accept any two elements of list or vector, and should not have any side effects. proc should return a true value when its first argument is strictly less than its second, and
#fotherwise.The
list-sortandvector-sortprocedures perform a stable sort of list or vector in ascending order according to proc, without changing list or vector in any way. Thelist-sortprocedure returns a list, andvector-sortreturns a vector.The results may be
eq?to the argument when the argument is already sorted, and the result oflist-sortmay share structure with a tail of the original list.The sorting algorithm performs O(n log n) calls to proc where n is the length of list or vector, and all arguments passed to proc are elements of the list or vector being sorted, but the pairing of arguments and the sequencing of calls to proc are not specified. If multiple returns occur from
list-sortorvector-sort, the return values returned by earlier returns are not mutated.(list-sort < '(3 5 2 1)) ⇒ (1 2 3 5) (vector-sort < '#(3 5 2 1)) ⇒ #(1 2 3 5)Implementation responsibilities: The implementation must check the restrictions on proc to the extent performed by applying it as described. An implementation may check whether proc is an appropriate argument before applying it.
proc should accept any two elements of the vector, and should not have any side effects. proc should return a true value when its first argument is strictly less than its second, and
#fotherwise.The
vector-sort!procedure destructively sorts vector in ascending order according to proc. The sorting algorithm performs O(n^2) calls to proc where n is the length of vector, and all arguments passed to proc are elements of the vector being sorted, but the pairing of arguments and the sequencing of calls to proc are not specified. The sorting algorithm may be unstable. The procedure returns unspecified values.(define v (vector 3 5 2 1)) (vector-sort! < v) ⇒ unspecified v ⇒ #(1 2 3 5)Implementation responsibilities: The implementation must check the restrictions on proc to the extent performed by applying it as described. An implementation may check whether proc is an appropriate argument before applying it.
This chapter describes the (rnrs control (6)) library, which provides
useful control structures.
<test> must be an expression.
A
whenexpression is evaluated by evaluating the <test> expression. If <test> evaluates to a true value, the remaining <expression>s are evaluated in order, and the results of the last <expression> are returned as the results of the entirewhenexpression. Otherwise, thewhenexpression returns unspecified values.An
unlessexpression is evaluated by evaluating the <test> expression. If <test> evaluates to#f, the remaining <expression>s are evaluated in order, and the results of the last <expression> are returned as the results of the entireunlessexpression. Otherwise, theunlessexpression returns unspecified values.The final <expression> is in tail context if the
whenorunlessform is itself in tail context.(when (> 3 2) 'greater) ⇒ greater (when (< 3 2) 'greater) ⇒ #<unspecified> (unless (> 3 2) 'less) ⇒ #<unspecified> (unless (< 3 2) 'less) ⇒ lessThe
whenandunlessexpressions are derived forms. They could be defined by the following macros:(define-syntax when (syntax-rules () ((when test result1 result2 ...) (if test (begin result1 result2 ...))))) (define-syntax unless (syntax-rules () ((unless test result1 result2 ...) (if (not test) (begin result1 result2 ...)))))
The <init>s, <step>s, <test>s, and <command>s must be expressions. The <variable>s must be pairwise distinct variables.
The
doexpression is an iteration construct. It specifies a set of variables to be bound, how they are to be initialized at the start, and how they are to be updated on each iteration.A
doexpression is evaluated as follows: The <init> expressions are evaluated (in some unspecified order), the <variable>s are bound to fresh locations, the results of the <init> expressions are stored in the bindings of the <variable>s, and then the iteration phase begins.Each iteration begins by evaluating <test>; if the result is
#f, then the <command>s are evaluated in order for effect, the <step> expressions are evaluated in some unspecified order, the <variable>s are bound to fresh locations holding the results, and the next iteration begins.If <test> evaluates to a true value, the <expression>s are evaluated from left to right and the values of the last <expression> are returned. If no <expression>s are present, then the
doexpression returns unspecified values.The region consists of the entire
doexpression except for the <init>s.A <step> may be omitted, in which case the effect is the same as if
(<variable> <init> <variable>)had been written instead of(<variable> <init>).If a
doexpression appears in a tail context, the <expression>s are a <tail sequence> in the sense of report section baselib tail call, i.e., the last <expression> is also in a tail context.(do ((vec (make-vector 5)) (i 0 (+ i 1))) ((= i 5) vec) (vector-set! vec i i)) ⇒ #(0 1 2 3 4) (let ((x '(1 3 5 7 9))) (do ((x x (cdr x)) (sum 0 (+ sum (car x)))) ((null? x) sum))) ⇒ 25The following definition of
douses a trick to expand the variable clauses.(define-syntax do (syntax-rules () ((do ((var init step ...) ...) (test expr ...) command ...) (letrec ((loop (lambda (var ...) (if test (begin #f ; avoid empty begin expr ...) (begin command ... (loop (do "step" var step ...) ...)))))) (loop init ...))) ((do "step" x) x) ((do "step" x y) y)))
Each <case-lambda clause> must be of the form:
(<formals> <body>)<formals> must be as in a
lambdaform (baselib expressions procedures), and <body> is as described in report section baselib bodies.A
case-lambdaexpression evaluates to a procedure. This procedure, when applied, tries to match its arguments to the <case-lambda clause>s in order. The arguments match a clause if one of the following conditions is fulfilled:
- <formals> has the form
(<variable> ...)and the number of arguments is the same as the number of formal parameters in <formals>.- <formals> has the form:
(<variable1> ... <variablen> . <variable_(n+1)>)and the number of arguments is at least n.
- <formals> has the form
<variable>.For the first clause matched by the arguments, the variables of the <formals> are bound to fresh locations containing the argument values in the same arrangement as with
lambda.The last expression of a <body> in a
case-lambdaexpression is in tail context.If the arguments match none of the clauses, an exception with condition type
&assertionis raised.(define foo (case-lambda (() 'zero) ((x) (list 'one x)) ((x y) (list 'two x y)) ((a b c d . e) (list 'four a b c d e)) (rest (list 'rest rest)))) (foo) ⇒ zero (foo 1) ⇒ (one 1) (foo 1 2) ⇒ (two 1 2) (foo 1 2 3) ⇒ (rest (1 2 3)) (foo 1 2 3 4) ⇒ (four 1 2 3 4 ())The
case-lambdakeyword can be defined in terms oflambdaby the following macros:(define-syntax case-lambda (syntax-rules () ((_ (fmls b1 b2 ...)) (lambda fmls b1 b2 ...)) ((_ (fmls b1 b2 ...) ...) (lambda args (let ((n (length args))) (case-lambda-help args n (fmls b1 b2 ...) ...)))))) (define-syntax case-lambda-help (syntax-rules () ((_ args n) (assertion-violation #f "unexpected number of arguments")) ((_ args n ((x ...) b1 b2 ...) more ...) (if (= n (length '(x ...))) (apply (lambda (x ...) b1 b2 ...) args) (case-lambda-help args n more ...))) ((_ args n ((x1 x2 ... . r) b1 b2 ...) more ...) (if (>= n (length '(x1 x2 ...))) (apply (lambda (x1 x2 ... . r) b1 b2 ...) args) (case-lambda-help args n more ...))) ((_ args n (r b1 b2 ...) more ...) (apply (lambda r b1 b2 ...) args))))
This section describes abstractions for creating new data types representing records.
A record is a compound data structure with a fixed number of components,
called fields. Each record has an associated type specified by a
record-type descriptor, which is an object that specifies the
fields of the record and various other properties that all records of
that type share. Record objects are created by a record
constructor, a procedure that creates a fresh record object and
initializes its fields to values. Records of different types can be
distinguished from each other and from other types of objects by
record predicates. A record predicate returns #t when
passed a record of the type specified by the record–type descriptor and
#f otherwise. An accessor extracts from a record the
component associated with a field, and a mutator changes the
component to a different value.
Record types can be extended via single inheritance, allowing record types to model hierarchies that occur in applications like algebraic data types as well as single–inheritance class systems. If a record type t extends another record type p, each record of type t is also a record of type p, and the predicate, accessors, and mutators applicable to a record of type p are also applicable to a record of type t. The extension relationship is transitive in the sense that a type extends its parent's parent, if any, and so on. A record type that does not extend another record type is called a base record type.
A record type can be sealed to prevent it from being extended. Moreover, a record type can be nongenerative, i.e., it is globally identified by a “uid”, and new, compatible definitions of a nongenerative record type with the same uid as a previous always yield the same record type.
The record mechanism spans three libraries:
(rnrs records syntactic (6))(rnrs records procedural (6))(rnrs records inspection (6))The inspection procedures allow programs to obtain from a record instance a descriptor for the type and from there obtain access to the fields of the record instance. This facility allows the creation of portable printers and inspectors. A program may prevent access to a record's type (and thereby protect the information stored in the record from the inspection mechanism) by declaring the type opaque. Thus, opacity as presented here can be used to enforce abstraction barriers.
Any of the standard types mentioned in this report may or may not be implemented as an opaque record type. Thus, it may be possible to use inspection on objects of the standard types.
The procedural layer is particularly useful for writing interpreters that construct host–compatible record types. It may also serve as a target for expansion of the syntactic layers. The record operations provided through the procedural layer may, however, be less efficient than the operations provided through the syntactic layer, which is designed to allow expand–time determination of record–instance sizes and field offsets. Therefore, alternative implementations of syntactic record–type definition should, when possible, expand into the syntactic layer rather than the procedural layer.
The syntactic layer is used more commonly and therefore described first. This chapter uses the rtd and constructor-descriptor parameter names for arguments that must be record-type descriptors and constructor descriptors, respectively.
The fields of a record type are designated mutable or immutable. Correspondingly, a record type with no mutable field is called immutable, and all records of that type are immutable objects. All other record types are mutable, and so are their records.
Each call to a record constructor returns a new record with a fresh
location (lang basic storage model). Consequently, for two
records obj1 and obj2, the return value of (eqv?
obj1 obj2), as well as the return value of (eq?
obj1 obj2), adheres to the following criteria
(baselib predicates):
eqv?), eqv? returns
#f.
eqv? returns #f.
eqv? returns #t.
eqv?
returns #f, then eqv? returns #f.
The syntactic layer is provided by the (rnrs records syntactic (6))
library. Some details of the specification are explained in terms of
the specification of the procedural layer. Procedural layer
The record–type–defining form define-record-type is a
definition and can appear anywhere any other <definition> can
appear.
A
define-record-typeform defines a record type along with associated constructor descriptor and constructor, predicate, field accessors, and field mutators. Thedefine-record-typeform expands into a set of definitions in the environment wheredefine-record-typeappears; hence, it is possible to refer to the bindings (except for that of the record type itself) recursively.The <name spec> specifies the names of the record type, constructor, and predicate. It must take one of the following forms:
(<record name> <constructor name> <predicate name>) <record name><record name>, <constructor name>, and <predicate name> must all be identifiers.
<record name>, taken as a symbol, becomes the name of the record type. (See the description of
make-record-type-descriptorbelow.) Additionally, it is bound by this definition to an expand–time or run–time representation of the record type and can be used as parent name in syntactic record–type definitions that extend this definition. It can also be used as a handle to gain access to the underlying record–type descriptor and constructor descriptor (seerecord-type-descriptorandrecord-constructor-descriptorbelow).<constructor name> is defined by this definition to be a constructor for the defined record type, with a protocol specified by the ‘protocol’ clause, or, in its absence, using a default protocol. For details, see the description of the ‘protocol’ clause below.
<predicate name> is defined by this definition to a predicate for the defined record type.
The second form of <name spec> is an abbreviation for the first form, where the name of the constructor is generated by prefixing the record name with ‘make-’, and the predicate name is generated by adding a question mark (‘?’) to the end of the record name. For example, if the record name is ‘frob’, the name of the constructor is
make-frob, and the predicate name isfrob?.Each <record clause> must take one of the following forms; it is a syntax violation if multiple <record clause>s of the same kind appear in a
define-record-typeform.
Each <field spec> has one of the following forms
(immutable <field name> <accessor name>) (mutable <field name> <accessor name> <mutator name>) (immutable <field name>) (mutable <field name>) <field name><field name>, <accessor name>, and <mutator name> must all be identifiers. The first form declares an immutable field called <field name>, with the corresponding accessor named <accessor name>. The second form declares a mutable field called <field name>, with the corresponding accessor named <accessor name>, and with the corresponding mutator named <mutator name>.
If <field spec> takes the third or fourth form, the accessor name is generated by appending the record name and field name with a hyphen separator, and the mutator name (for a mutable field) is generated by adding a ‘-set!’ suffix to the accessor name. For example, if the record name is ‘frob’ and the field name is ‘widget’, the accessor name is
frob-widgetand the mutator name isfrob-widget-set!.If <field spec> is just a <field name> form, it is an abbreviation for
(immutable <field name>).The <field name>s become, as symbols, the names of the fields in the record–type descriptor being created, in the same order.
The ‘fields’ clause may be absent; this is equivalent to an empty ‘fields’ clause.
Specifies that the record type is to have parent type <parent name>, where <parent name> is the <record name> of a record type previously defined using
define-record-type. The record–type definition associated with <parent name> must not be sealed. If no ‘parent’ clause and no ‘parent-rtd’ (stdlib records procedural layer) clause is present, the record type is a base type.
<expression> is evaluated in the same environment as the
define-record-typeform, and must evaluate to a protocol appropriate for the record type being defined.The protocol is used to create a record–constructor descriptor as described below. If no ‘protocol’ clause is specified, a constructor descriptor is still created using a default protocol. The clause can be absent only if the record type being defined has no parent type, or if the parent definition does not specify a protocol.
If this option is specified with operand
#t, the defined record type is sealed, i.e., no extensions of the record type can be created. If this option is specified with operand#f, or is absent, the defined record type is not sealed.
If this option is specified with operand
#t, or if an opaque parent record type is specified, the defined record type is opaque. Otherwise, the defined record type is not opaque. See the specification of ‘record-rtd’ below for details.
This specifies that the record type is nongenerative with uid <uid>, which must be an <identifier>. If <uid> is absent, a unique uid is generated at macro–expansion time. If two record–type definitions specify the same uid, then the record–type definitions should be equivalent, i.e., the implied arguments to
make-record-type-descriptormust be equivalent as described undermake-record-type-descriptor.If this condition is not met, it is either considered a syntax violation or an exception with condition type ‘&assertion’ is raised. If the condition is met, a single record type is generated for both definitions.
In the absence of a ‘nongenerative’ clause, a new record type is generated every time a
define-record-typeform is evaluated:(let ((f (lambda (x) (define-record-type r ...) (if x r? (make-r ...))))) ((f #t) (f #f))) ⇒ #f
Specifies that the record type is to have its parent type specified by <parent rtd>, which should be an expression evaluating to a record–type descriptor, and <parent cd>, which should be an expression evaluating to a constructor descriptor (
make-record-constructor-descriptor). Either <parent rtd> or <parent cd> can evaluate to#f. The record–type definition associated with the value of <parent rtd> must not be sealed. Moreover, a record–type definition must not have both a ‘parent’ and a ‘parent-rtd’ clause.
Note The syntactic layer is designed to allow record–instance
sizes and field offsets to be determined at expand time, i.e., by a
macro definition of define-record-type, as long as the parent (if
any) is known. Implementations that take advantage of this may generate
less efficient constructor, accessor, and mutator code when the
‘parent-rtd’ clause is used, since the type of the parent is
generally not known until run time. The ‘parent’ clause should
therefore be used instead when possible.
All bindings created by define-record-type (for the record type,
the constructor, the predicate, the accessors, and the mutators) must
have names that are pairwise distinct.
The constructor created by a define-record-type form is a
procedure as follows:
define-record-type form
with a procedure p as its argument. It should return a procedure,
which will become the constructor bound to <constructor name>. The
procedure p accepts as many arguments as there are fields, in the
same order as they appear in the ‘fields’ clause, and returns a
record object with the fields initialized to the corresponding
arguments.
The constructor returned by the protocol procedure can accept an arbitrary number of arguments, and should call p once to construct a record object, and return that record object.
For example, the following protocol expression for a record–type definition with three fields creates a constructor that accepts values for all fields, and initialized them in the reverse order of the arguments:
(lambda (p)
(lambda (v1 v2 v3)
(p v3 v2 v1)))
The constructor returned by the protocol procedure can accept an arbitrary number of arguments, and should call n once to construct the procedure p, and call p once to create the record object, and finally return that record object.
For example, the following protocol expression assumes that the constructor of the parent type takes three arguments:
(lambda (n)
(lambda (v1 v2 v3 x1 x2 x3 x4)
(let ((p (n v1 v2 v3)))
(p x1 x2 x3 x4))))
The resulting constructor accepts seven arguments, and initializes the fields of the parent types according to the constructor of the parent type, with v1, v2, and v3 as arguments. It also initializes the fields of this record type to the values of x1, ..., x4.
A protocol may perform other actions consistent with the requirements described above, including mutation of the new record or other side effects, before returning the record.
Any definition that takes advantage of implicit naming for the constructor, predicate, accessor, and mutator names can be rewritten trivially to a definition that specifies all names explicitly. For example, the implicit–naming record definition:
(define-record-type frob
(fields (mutable widget))
(protocol
(lambda (p)
(lambda (n) (p (make-widget n))))))
is equivalent to the following explicit–naming record definition.
(define-record-type (frob make-frob frob?)
(fields (mutable widget
frob-widget
frob-widget-set!))
(protocol
(lambda (p)
(lambda (n) (p (make-widget n))))))
Also, the implicit–naming record definition:
(define-record-type point (fields x y))
is equivalent to the following explicit–naming record definition:
(define-record-type (point make-point point?)
(fields
(immutable x point-x)
(immutable y point-y)))
With implicit naming, it is still possible to specify some of the names explicitly; for example, the following overrides the choice of accessor and mutator names for the widget field.
(define-record-type frob
(fields (mutable widget getwid setwid!))
(protocol
(lambda (p)
(lambda (n) (p (make-widget n))))))
Evaluates to the record–type descriptor (see below) associated with the type specified by <record name>.
NOTE Therecord-type-descriptorprocedure works on both opaque and non–opaque record types.
Evaluates to the record–constructor descriptor (see below) associated with <record name>.
The following example uses the record? procedure from the
(rnrs records inspection (6)) library:
(define-record-type (point make-point point?)
(fields (immutable x point-x)
(mutable y point-y set-point-y!))
(nongenerative
point-4893d957-e00b-11d9-817f-00111175eb9e))
(define-record-type (cpoint make-cpoint cpoint?)
(parent point)
(protocol
(lambda (n)
(lambda (x y c)
((n x y) (color->rgb c)))))
(fields
(mutable rgb cpoint-rgb cpoint-rgb-set!)))
(define (color->rgb c)
(cons 'rgb c))
(define p1 (make-point 1 2))
(define p2 (make-cpoint 3 4 'red))
(point? p1) ⇒ #t
(point? p2) ⇒ #t
(point? (vector)) ⇒ #f
(point? (cons 'a 'b)) ⇒ #f
(cpoint? p1) ⇒ #f
(cpoint? p2) ⇒ #f
(point-x p1) ⇒ 1
(point-y p1) ⇒ 2
(point-x p2) ⇒ 3
(point-y p2) ⇒ 4
(cpoint-rgb p2) ⇒ (rgb . red)
(set-point-y! p1 17) ⇒ unspecified
(point-y p1) ⇒ 17
(record-rtd p1)
⇒ (record-type-descriptor point)
(define-record-type (ex1 make-ex1 ex1?)
(protocol (lambda (p) (lambda a (p a))))
(fields (immutable f ex1-f)))
(define ex1-i1 (make-ex1 1 2 3))
(ex1-f ex1-i1) ⇒ (1 2 3)
(define-record-type (ex2 make-ex2 ex2?)
(protocol
(lambda (p) (lambda (a . b) (p a b))))
(fields (immutable a ex2-a)
(immutable b ex2-b)))
(define ex2-i1 (make-ex2 1 2 3))
(ex2-a ex2-i1) ⇒ 1
(ex2-b ex2-i1) ⇒ (2 3)
(define-record-type (unit-vector
make-unit-vector
unit-vector?)
(protocol
(lambda (p)
(lambda (x y z)
(let ((length
(sqrt (+ (* x x)
(* y y)
(* z z)))))
(p (/ x length)
(/ y length)
(/ z length))))))
(fields (immutable x unit-vector-x)
(immutable y unit-vector-y)
(immutable z unit-vector-z)))
(define *ex3-instance* #f)
(define-record-type ex3
(parent cpoint)
(protocol
(lambda (n)
(lambda (x y t)
(let ((r ((n x y 'red) t)))
(set! *ex3-instance* r)
r))))
(fields
(mutable thickness))
(sealed #t) (opaque #t))
(define ex3-i1 (make-ex3 1 2 17))
(ex3? ex3-i1) ⇒ #t
(cpoint-rgb ex3-i1) ⇒ (rgb . red)
(ex3-thickness ex3-i1) ⇒ 17
(ex3-thickness-set! ex3-i1 18) ⇒ unspecified
(ex3-thickness ex3-i1) ⇒ 18
*ex3-instance* ⇒ ex3-i1
(record? ex3-i1) ⇒ #f
The procedural layer is provided by the (rnrs records procedural (6))
library.
Return a record-type descriptor (RTD) representing a record type distinct from all built–in types and other record types.
The name argument must be a symbol. It names the record type, and is intended purely for informational purposes and may be used for printing by the underlying Scheme system.
The parent argument must be either
#for an RTD. If it is an RTD, the returned record type, t, extends the record type p represented by parent. An exception with condition type ‘&assertion’ is raised if parent is sealed (see below).The uid argument must be either
#for a symbol. If uid is a symbol, the record–creation operation is nongenerative i.e., a new record type is created only if no previous call tomake-record-type-descriptorwas made with the uid. If uid is#f, the record–creation operation is generative, i.e., a new record type is created even if a previous call tomake-record-type-descriptorwas made with the same arguments.If
make-record-type-descriptoris called twice with the same uid symbol, the parent arguments in the two calls must beeqv?, the fields argumentsequal?, the sealed? arguments boolean–equivalent (both#for both true), and the opaque? arguments boolean–equivalent. If these conditions are not met, an exception with condition type ‘&assertion’ is raised when the second call occurs. If they are met, the second call returns, without creating a new record type, the same record–type descriptor (in the sense ofeqv?) as the first call.NOTE Users are encouraged to use symbol names constructed using the UUID namespace (for example, using the record–type name as a prefix) for the uid argument.The sealed? flag must be a boolean. If true, the returned record type is sealed, i.e., it cannot be extended.
The opaque? flag must be a boolean. If true, the record type is opaque. If passed an instance of the record type,
record?returns#f. Moreover, if ‘record-rtd’ (see “Inspection” below) is called with an instance of the record type, an exception with condition type ‘&assertion’ is raised. The record type is also opaque if an opaque parent is supplied. If opaque? is#fand an opaque parent is not supplied, the record is not opaque.The fields argument must be a vector of field specifiers. Each field specifier must be a list of the form
(mutablename)or a list of the form(immutablename). Each name must be a symbol and names the corresponding field of the record type; the names need not be distinct. A field identified as mutable may be modified, whereas, when a program attempts to obtain a mutator for a field identified as immutable, an exception with condition type ‘&assertion’ is raised. Where field order is relevant, e.g., for record construction and field access, the fields are considered to be ordered as specified, although no particular order is required for the actual representation of a record instance.The specified fields are added to the parent fields, if any, to determine the complete set of fields of the returned record type. If fields is modified after
make-record-type-descriptorhas been called, the effect on the returned RTD is unspecified.A generative record–type descriptor created by a call to
make-record-type-descriptoris noteqv?to any record–type descriptor (generative or nongenerative) created by another call tomake-record-type-descriptor. A generative record–type descriptor iseqv?only to itself, i.e.,(eqv?rtd1 rtd2)if, and only if,(eq?rtd1 rtd2). Also, two nongenerative record–type descriptors areeqv?if, and only if, they were created by calls tomake-record-type-descriptorwith the same uid arguments.
Return
#tif the argument is a record–type descriptor,#fotherwise.
Return a record-constructor descriptor (or constructor descriptor for short) that specifies a record constructor (or constructor for short), that can be used to construct record values of the type specified by rtd, and which can be obtained via
record-constructor. A constructor descriptor can also be used to create other constructor descriptors for subtypes of its own record type. rtd must be a record–type descriptor. protocol must be a procedure or#f. If it is#f, a default protocol procedure is supplied.If protocol is a procedure, it is handled analogously to the protocol expression in a
define-record-typeform.If rtd is a base record type parent-constructor-descriptor must be
#f. In this case, protocol is called byrecord-constructorwith a single argument p. p is a procedure that expects one argument for every field of rtd and returns a record with the fields of rtd initialized to these arguments. The procedure returned by protocol should call p once with the number of arguments p expects and return the resulting record as shown in the simple example below:(lambda (p) (lambda (v1 v2 v3) (p v1 v2 v3)))Here, the call to p returns a record whose fields are initialized with the values of v1, v2, and v3. The expression above is equivalent to
(lambda (p) p). Note that the procedure returned by protocol is otherwise unconstrained; specifically, it can take any number of arguments.If rtd is an extension of another record type parent-rtd and protocol is a procedure, parent-constructor-descriptor must be a constructor descriptor of parent-rtd or
#f. If parent-constructor-descriptor is a constructor descriptor, protocol it is called byrecord-constructorwith a single argument n, which is a procedure that accepts the same number of arguments as the constructor of parent-constructor-descriptor and returns a procedure p that, when called, constructs the record itself. The p procedure expects one argument for every field of rtd (not including parent fields) and returns a record with the fields of rtd initialized to these arguments, and the fields of parent-rtd and its parents initialized as specified by parent-constructor-descriptor.The procedure returned by protocol should call n once with the number of arguments n expects, call the procedure p it returns once with the number of arguments p expects and return the resulting record. A simple protocol in this case might be written as follows:
(lambda (n) (lambda (v1 v2 v3 x1 x2 x3 x4) (let ((p (n v1 v2 v3))) (p x1 x2 x3 x4))))This passes arguments v1, v2, v3 to n for parent-constructor-descriptor and calls
pwith x1, ..., x4 to initialize the fields of rtd itself.Thus, the constructor descriptors for a record type form a sequence of protocols parallel to the sequence of record–type parents. Each constructor descriptor in the chain determines the field values for the associated record type. Child record constructors need not know the number or contents of parent fields, only the number of arguments accepted by the parent constructor.
protocol may be
#f, specifying a default constructor that accepts one argument for each field of rtd (including the fields of its parent type, if any). Specifically, if rtd is a base type, the default protocol procedure behaves as if it were(lambda (p) p). If rtd is an extension of another type, then parent-constructor-descriptor must be either#for itself specify a default constructor, and the default protocol procedure behaves as if it were:(lambda (n) (lambda (v1 ... vj x1 ... xk) (let ((p (n v1 ... vj))) (p x1 ... xk))))The resulting constructor accepts one argument for each of the record type's complete set of fields (including those of the parent record type, the parent's parent record type, etc.) and returns a record with the fields initialized to those arguments, with the field values for the parent coming before those of the extension in the argument list. (In the example, j is the complete number of fields of the parent type, and $k$ is the number of fields of rtd itself.)
If rtd is an extension of another record type and parent-constructor-descriptor is
#f, parent-constructor-descriptor is treated as if it were a constructor descriptor for the parent rtd of rtd with a default protocol.Implementation responsibilities: If protocol is a procedure, the implementation must check the restrictions on it to the extent performed by applying it as described when the constructor is called. An implementation may check whether protocol is an appropriate argument before applying it.
(define rtd1 (make-record-type-descriptor 'rtd1 #f #f #f #f '#((immutable x1) (immutable x2)))) (define rtd2 (make-record-type-descriptor 'rtd2 rtd1 #f #f #f '#((immutable x3) (immutable x4)))) (define rtd3 (make-record-type-descriptor 'rtd3 rtd2 #f #f #f '#((immutable x5) (immutable x6)))) (define protocol1 (lambda (p) (lambda (a b c) (p (+ a b) (+ b c))))) (define protocol2 (lambda (n) (lambda (a b c d e f) (let ((p (n a b c))) (p (+ d e) (+ e f)))))) (define protocol3 (lambda (n) (lambda (a b c d e f g h i) (let ((p (n a b c d e f))) (p (+ g h) (+ h i)))))) (define cd1 (make-record-constructor-descriptor rtd1 #f protocol1)) (define cd2 (make-record-constructor-descriptor rtd2 cd1 protocol2)) (define cd3 (make-record-constructor-descriptor rtd3 cd2 protocol3)) (define make-rtd1 (record-constructor cd1)) (define make-rtd2 (record-constructor cd2)) (define make-rtd3 (record-constructor cd3)) (make-rtd3 1 2 3 4 5 6 7 8 9) ⇒ #<record with fields initialized to 3, 5, 9, 11, 15, 17>
Call the protocol of constructor-descriptor (as described for
make-record-constructor-descriptor) and returns the resulting constructor constructor for records of the record type associated with constructor-descriptor.
Return a procedure that, given an object obj, returns
#tif obj is a record of the type represented by rtd, and#fotherwise.
k must be a valid field index of rtd. The
record-accessorprocedure returns a one–argument procedure whose argument must be a record of the type represented by rtd. This procedure returns the value of the selected field of that record.The field selected corresponds to the kth element (0–based) of the fields argument to the invocation of
make-record-type-descriptorthat created rtd. Note that k cannot be used to specify a field of any type rtd extends.
k must be a valid field index of rtd. The
record-mutatorprocedure returns a two–argument procedure whose arguments must be a record record r of the type represented by rtd and an object obj. This procedure stores obj within the field of r specified by k. The k argument is as inrecord-accessor. If k specifies an immutable field, an exception with condition type ‘&assertion’ is raised. The mutator returns unspecified values.
(define :point
(make-record-type-descriptor
'point #f
#f #f #f
'#((mutable x) (mutable y))))
(define :point-cd
(make-record-constructor-descriptor :point #f #f))
(define make-point (record-constructor :point-cd))
(define point? (record-predicate :point))
(define point-x (record-accessor :point 0))
(define point-y (record-accessor :point 1))
(define point-x-set! (record-mutator :point 0))
(define point-y-set! (record-mutator :point 1))
(define p1 (make-point 1 2))
(point? p1) ⇒ #t
(point-x p1) ⇒ 1
(point-y p1) ⇒ 2
(point-x-set! p1 5) ⇒ unspecified
(point-x p1) ⇒ 5
(define :point2
(make-record-type-descriptor
'point2 :point
#f #f # f '#((mutable x) (mutable y))))
(define make-point2
(record-constructor
(make-record-constructor-descriptor :point2
#f #f)))
(define point2? (record-predicate :point2))
(define point2-xx (record-accessor :point2 0))
(define point2-yy (record-accessor :point2 1))
(define p2 (make-point2 1 2 3 4))
(point? p2) ⇒ #t
(point-x p2) ⇒ 1
(point-y p2) ⇒ 2
(point2-xx p2) ⇒ 3
(point2-yy p2) ⇒ 4
(define :point-cd/abs
(make-record-constructor-descriptor
:point #f
(lambda (new)
(lambda (x y)
(new (abs x) (abs y))))))
(define make-point/abs
(record-constructor :point-cd/abs))
(point-x (make-point/abs -1 -2)) ⇒ 1
(point-y (make-point/abs -1 -2)) ⇒ 2
(define :cpoint
(make-record-type-descriptor
'cpoint :point
#f #f #f
'#((mutable rgb))))
(define make-cpoint
(record-constructor
(make-record-constructor-descriptor
:cpoint :point-cd
(lambda (p)
(lambda (x y c)
((p x y) (color->rgb c)))))))
(define make-cpoint/abs
(record-constructor
(make-record-constructor-descriptor
:cpoint :point-cd/abs
(lambda (p)
(lambda (x y c)
((p x y) (color->rgb c)))))))
(define cpoint-rgb
(record-accessor :cpoint 0))
(define (color->rgb c)
(cons 'rgb c))
(cpoint-rgb (make-cpoint -1 -3 'red)) ⇒ (rgb . red)
(point-x (make-cpoint -1 -3 'red)) ⇒ -1
(point-x (make-cpoint/abs -1 -3 'red)) ⇒ 1
The (rnrs records inspection (6)) library provides procedures for
inspecting records and their record–type descriptors. These procedures
are designed to allow the writing of portable printers and inspectors.
On the one hand, record? and record-rtd treat records of
opaque record types as if they were not records. On the other hand, the
inspection procedures that operate on record–type descriptors
themselves are not affected by opacity. In other words, opacity
controls whether a program can obtain an rtd from a record. If the
program has access to the original rtd via
make-record-type-descriptor or record-type-descriptor, it
can still make use of the inspection procedures.
Return
#tif obj is a record, and its record type is not opaque, and return#fotherwise.
Return the rtd representing the type of record if the type is not opaque. The rtd of the most precise type is returned; that is, the type t such that record is of type t but not of any type that extends t. If the type is opaque, an exception is raised with condition type
&assertion.
Return the parent of the record–type descriptor rtd, or
#fif it has none.
Return the uid of the record–type descriptor rtd, or
#fif it has none. (An implementation may assign a generated uid to a record type even if the type is generative, so the return of a uid does not necessarily imply that the type is nongenerative.)
Return
#tif the record-type descriptor is sealed, and#fif not.
Return
#tif the the record-type descriptor is opaque, and#fif not.
Return a vector of symbols naming the fields of the type represented by rtd (not including the fields of parent types) where the fields are ordered as described under
make-record-type-descriptor. The returned vector may be immutable. If the returned vector is modified, the effect on rtd is unspecified.
Returns
#tif the field specified by k of the type represented by rtd is mutable, and#fif not. k is as inrecord-accessor.
Scheme allows programs to deal with exceptional situations using two cooperating facilities: The exception system for raising and handling exceptional situations, and the condition system for describing these situations.
The exception system allows the program, when it detects an exceptional situation, to pass control to an exception handler, and to dynamically establish such exception handlers. Exception handlers are always invoked with an object describing the exceptional situation. Scheme's condition system provides a standardized taxonomy of such descriptive objects, as well as a facility for extending the taxonomy.
This section describes Scheme's exception–handling and
exception–raising constructs provided by the (rnrs exceptions (6))
library.
Exception handlers are one–argument procedures that determine the action the program takes when an exceptional situation is signalled. The system implicitly maintains a current exception handler.
The program raises an exception by invoking the current exception handler, passing it an object encapsulating information about the exception. Any procedure accepting one argument may serve as an exception handler and any object may be used to represent an exception.
The system maintains the current exception handler as part of the dynamic environment of the program (see lang basic dynamic extent).
When a program begins its execution, the current exception handler is
expected to handle all &serious conditions by interrupting
execution, reporting that an exception has been raised, and displaying
information about the condition object that was provided. The handler
may then exit, or may provide a choice of other options. Moreover, the
exception handler is expected to return when passed any other
non–&serious condition. Interpretation of these expectations
necessarily depends upon the nature of the system in which programs are
executed, but the intent is that users perceive the raising of an
exception as a controlled escape from the situation that raised the
exception, not as a crash.
handler must be a procedure and should accept one argument. thunk must be a procedure that accepts zero arguments. The
with-exception-handlerprocedure returns the results of invoking thunk. handler is installed as the current exception handler for the dynamic extent (as determined bydynamic-wind) of the invocation of thunk.Implementation responsibilities: The implementation must check the restrictions on handler to the extent performed by applying it as described when it is called as a result of a call to
raiseorraise-continuable. An implementation may check whether handler is an appropriate argument before applying it.
Each <cond clause> is as in the specification of
cond. (See report section baselib expressions derived cond.) ‘=>’ and ‘else’ are the same as in the(rnrs base (6))library.Evaluating a
guardform evaluates <body> with an exception handler that binds the raised object to <variable> and within the scope of that binding evaluates the clauses as if they were the clauses of acondexpression. That implicitcondexpression is evaluated with the continuation and dynamic environment of theguardform. If every <cond clause>'s <test> evaluates to#fand there is noelseclause, thenraise-continuableis re–invoked on the raised object within the dynamic environment of the original call toraiseexcept that the current exception handler is that of theguardexpression.The final expression in a <cond> clause is in a tail context if the
guardexpression itself is.
Raise a non–continuable exception by invoking the current exception handler on obj. The handler is called with a continuation whose dynamic environment is that of the call to
raise, except that the current exception handler is the one that was in place when the handler being called was installed. When the handler returns, a non–continuable exception with condition type&non-continuableis raised in the same dynamic environment as the handler.
Raise a continuable exception by invoking the current exception handler on obj. The handler is called with a continuation that is equivalent to the continuation of the call to
raise-continuable, with these two exceptions:
- The current exception handler is the one that was in place when the handler being called was installed.
- If the handler being called returns, then it will again become the current exception handler.
If the handler returns, the values it returns become the values returned by the call to
raise-continuable.
(guard (con
((error? con)
(if (message-condition? con)
(display (condition-message con))
(display "an error has occurred"))
'error)
((violation? con)
(if (message-condition? con)
(display (condition-message con))
(display "the program has a bug"))
'violation))
(raise
(condition
(make-error)
(make-message-condition "I am an error"))))
;; prints: I am an error
⇒ error
(guard (con
((error? con)
(if (message-condition? con)
(display (condition-message con))
(display "an error has occurred"))
'error))
(raise
(condition
(make-violation)
(make-message-condition "I am an error"))))
⇒ exception &violation
(guard (con
((error? con)
(display "error opening file")
#f))
(call-with-input-file "foo.scm" read))
;; prints: error opening file
⇒ #f
(with-exception-handler
(lambda (con)
(cond
((not (warning? con))
(raise con))
((message-condition? con)
(display (condition-message con)))
(else
(display "a warning has been issued")))
42)
(lambda ()
(+ (raise-continuable
(condition
(make-warning)
(make-message-condition
"should be a number")))
23)))
;; prints: should be a number
⇒ 65
This section describes Scheme's (rnrs conditions (6)) library for
creating and inspecting condition types and values. A condition value
encapsulates information about an exceptional situation. Scheme also
defines a number of basic condition types.
Scheme conditions provides two mechanisms to enable communication about an exceptional situation: subtyping among condition types allows handling code to determine the general nature of an exception even though it does not anticipate its exact nature, and compound conditions allow an exceptional situation to be described in multiple ways.
Conceptually, there are two different kinds of condition objects:
simple conditions and compound conditions. An object that
is either a simple condition or a compound condition is simply a
condition. Compound conditions form a type disjoint from the
base types described in report section baselib types. A simple
condition describes a single aspect of an exceptional situation. A
compound condition represents multiple aspects of an exceptional
situation as a list of simple conditions, its components. Most
of the operations described in this section treat a simple condition
identically to a compound condition with itself as its own sole
component. For a subtype t of &condition, a
condition of type t is either a record of type t or a
compound condition containing a component of type t.
Simple conditions are records of subtypes of the
&conditionrecord type. The&conditiontype has no fields and is neither sealed nor opaque.
The
conditionprocedure returns a condition object with the components of the conditions as its components, in the same order, i.e., with the components of condition1 appearing first in the same order as in condition1, then with the components of condition2, and so on. The returned condition is compound if the total number of components is zero or greater than one. Otherwise, it may be compound or simple.
The
simple-conditionsprocedure returns a list of the components of condition, in the same order as they appeared in the construction of condition. The returned list is immutable. If the returned list is modified, the effect on condition is unspecified.NOTE Becauseconditiondecomposes its arguments into simple conditions,simple-conditionsalways returns a “flattened” list of simple conditions.
Return
#tif obj is a (simple or compound) condition, otherwise returns#f.
rtd must be a record–type descriptor of a subtype of
&condition. Thecondition-predicateprocedure returns a procedure that takes one argument. This procedure returns#tif its argument is a condition of the condition type represented by rtd, i.e., if it is either a simple condition of that record type (or one of its subtypes) or a compound conditition with such a simple condition as one of its components, and#fotherwise.
rtd must be a record–type descriptor of a subtype of
&condition. proc should accept one argument, a record of the record type of rtd. Thecondition-accessorprocedure returns a procedure that accepts a single argument, which must be a condition of the type represented by rtd. This procedure extracts the first component of the condition of the type represented by rtd, and returns the result of applying proc to that component.
(define-record-type (&cond1 make-cond1 real-cond1?)
(parent &condition)
(fields
(immutable x real-cond1-x)))
(define cond1?
(condition-predicate
(record-type-descriptor &cond1)))
(define cond1-x
(condition-accessor
(record-type-descriptor &cond1)
real-cond1-x))
(define foo (make-cond1 'foo))
(condition? foo) ⇒ #t
(cond1? foo) ⇒ #t
(cond1-x foo) ⇒ foo
(define-record-type (&cond2 make-cond2 real-cond2?)
(parent &condition)
(fields
(immutable y real-cond2-y)))
(define cond2?
(condition-predicate
(record-type-descriptor &cond2)))
(define cond2-y
(condition-accessor
(record-type-descriptor &cond2)
real-cond2-y))
(define bar (make-cond2 'bar))
(condition? (condition foo bar)) ⇒ #t
(cond1? (condition foo bar)) ⇒ #t
(cond2? (condition foo bar)) ⇒ #t
(cond1? (condition foo)) ⇒ #t
(real-cond1? (condition foo)) ⇒ unspecified
(real-cond1? (condition foo bar)) ⇒ #f
(cond1-x (condition foo bar)) ⇒ foo
(cond2-y (condition foo bar)) ⇒ bar
(equal? (simple-conditions (condition foo bar))
(list foo bar)) ⇒ #t
(equal? (simple-conditions
(condition foo (condition bar)))
(list foo bar)) ⇒ #t
<condition-type>, <supertypes>, <constructor>, and <predicate> must all be identifiers. Each <field-spec> must be of the form
(<field> <accessor>)where both <field> and <accessor> must be identifiers.
The
define-condition-typeform expands into a record–type definition for a record type <condition-type>. The record type will be non–opaque, non–sealed, and its fields will be immutable. It will have <supertype> has its parent type. The remaining identifiers will be bound as follows:
- <constructor> is bound to a default constructor for the type: It accepts one argument for each of the record type's complete set of fields (including parent types, with the fields of the parent coming before those of the extension in the arguments) and returns a condition object initialized to those arguments.
- <predicate> is bound to a predicate that identifies conditions of type <condition-type> or any of its subtypes.
- Each <accessor> is bound to a procedure that extracts the corresponding field from a condition of type <condition-type>.
(define-condition-type &c &condition
make-c c?
(x c-x))
(define-condition-type &c1 &c
make-c1 c1?
(a c1-a))
(define-condition-type &c2 &c
make-c2 c2?
(b c2-b))
(define v1 (make-c1 "V1" "a1"))
(c? v1) ⇒ #t
(c1? v1) ⇒ #t
(c2? v1) ⇒ #f
(c-x v1) ⇒ "V1"
(c1-a v1) ⇒ "a1"
(define v2 (make-c2 "V2" "b2"))
(c? v2) ⇒ #t
(c1? v2) ⇒ #f
(c2? v2) ⇒ #t
(c-x v2) ⇒ "V2"
(c2-b v2) ⇒ "b2"
(define v3 (condition
(make-c1 "V3/1" "a3")
(make-c2 "V3/2" "b3")))
(c? v3) ⇒ #t
(c1? v3) ⇒ #t
(c2? v3) ⇒ #t
(c-x v3) ⇒ "V3/1"
(c1-a v3) ⇒ "a3"
(c2-b v3) ⇒ "b3"
(define v4 (condition v1 v2))
(c? v4) ⇒ #t
(c1? v4) ⇒ #t
(c2? v4) ⇒ #t
(c-x v4) ⇒ "V1"
(c1-a v4) ⇒ "a1"
(c2-b v4) ⇒ "b2"
(define v5 (condition v2 v3))
(c? v5) ⇒ #t
(c1? v5) ⇒ #t
(c2? v5) ⇒ #t
(c-x v5) ⇒ "V2"
(c1-a v5) ⇒ "a3"
(c2-b v5) ⇒ "b2"
Hierarchy of standard condition types:
&condition
|
+-----> &warning
|
+-----> &message
| &irritants
| &who
|
+-----> &serious
|
+-----> &error
|
+-----> &violation
|
+-----> &assertion
|
+-----> &non-continuable
|
+-----> &implementation-restriction
|
+-----> &lexical
|
+-----> &syntax
|
-----> &undefined
This condition type could be defined by:
(define-condition-type &message &condition make-message-condition message-condition? (message condition-message))It carries a message further describing the nature of the condition to humans.
This condition type could be defined by:
(define-condition-type &warning &condition make-warning warning?)This type describes conditions that do not, in principle, prohibit immediate continued execution of the program, but may interfere with the program's execution later.
This condition type could be defined by:
(define-condition-type &serious &condition make-serious-condition serious-condition?)This type describes conditions serious enough that they cannot safely be ignored. This condition type is primarily intended as a supertype of other condition types.
This condition type could be defined by:
(define-condition-type &error &serious make-error error?)This type describes errors, typically caused by something that has gone wrong in the interaction of the program with the external world or the user.
This condition type could be defined by:
(define-condition-type &violation &serious make-violation violation?)This type describes violations of the language standard or a library standard, typically caused by a programming error.
This condition type could be defined by:
(define-condition-type &assertion &violation make-assertion-violation assertion-violation?)This type describes an invalid call to a procedure, either passing an invalid number of arguments, or passing an argument of the wrong type.
This condition type could be defined by:
(define-condition-type &irritants &condition make-irritants-condition irritants-condition? (irritants condition-irritants))irritants should be a list of objects. This condition provides additional information about a condition, typically the argument list of a procedure that detected an exception. Conditions of this type are created by the
errorandassertion-violationprocedures of report section baselib errors.
This condition type could be defined by:
(define-condition-type &who &condition make-who-condition who-condition? (who condition-who))who should be a symbol or string identifying the entity reporting the exception. Conditions of this type are created by the
errorandassertion-violationprocedures (report section baselib errors), and thesyntax-violationprocedure (section lang basic syntax violations).
This condition type could be defined by:
(define-condition-type &non-continuable &violation make-non-continuable-violation non-continuable-violation?)This type indicates that an exception handler invoked via
raisehas returned.
This condition type could be defined by:
(define-condition-type &implementation-restriction &violation make-implementation-restriction-violation implementation-restriction-violation?)This type describes a violation of an implementation restriction allowed by the specification, such as the absence of representations for NaNs and infinities.
This condition type could be defined by:
(define-condition-type &lexical &violation make-lexical-violation lexical-violation?)This type describes syntax violations at the level of the datum syntax.
This condition type could be defined by:
(define-condition-type &syntax &violation make-syntax-violation syntax-violation? (form syntax-violation-form) (subform syntax-violation-subform))This type describes syntax violations. form should be the erroneous syntax object or a datum representing the code of the erroneous form. subform should be an optional syntax object or datum within the erroneous form that more precisely locates the violation. It can be
#fto indicate the absence of more precise information.
This condition type could be defined by:
(define-condition-type &undefined &violation make-undefined-violation undefined-violation?)This type describes unbound identifiers in the program.
This chapter describes Scheme's libraries for performing input and output:
(rnrs io ports (6))(rnrs io simple (6))(rnrs io ports (6)) library for
textual I/O, compatible with the traditional Scheme I/O procedures.
The section on conditions defines a condition–type hierarchy that is
exported by both the (rnrs io ports (6)) and (rnrs io simple (6))
libraries.
The procedures described in this chapter, when they detect an
exceptional situation that arises from an “I/O errors”, raise an
exception with condition type &i/o.
The condition types and corresponding predicates and accessors are
exported by both the (rnrs io ports (6)) and (rnrs io simple (6))
libraries. They are also exported by the (rnrs files (6)) library.
This condition type could be defined by:
(define-condition-type &i/o &error make-i/o-error i/o-error?)This is a supertype for a set of more specific I/O errors.
This condition type could be defined by:
(define-condition-type &i/o-read &i/o make-i/o-read-error i/o-read-error?)This condition type describes read errors that occurred during an I/O operation.
This condition type could be defined by:
(define-condition-type &i/o-write &i/o make-i/o-write-error i/o-write-error?)This condition type describes write errors that occurred during an I/O operation.
This condition type could be defined by:
(define-condition-type &i/o-invalid-position &i/o make-i/o-invalid-position-error i/o-invalid-position-error? (position i/o-error-position))This condition type describes attempts to set the file position to an invalid position. position should be the file position that the program intended to set. This condition describes a range error, but not an assertion violation.
This condition type could be defined by:
(define-condition-type &i/o-filename &i/o make-i/o-filename-error i/o-filename-error? (filename i/o-error-filename))This condition type describes an I/O error that occurred during an operation on a named file. filename should be the name of the file.
This condition type could be defined by:
(define-condition-type &i/o-file-protection &i/o-filename make-i/o-file-protection-error i/o-file-protection-error?)A condition of this type specifies that an operation tried to operate on a named file with insufficient access rights.
This condition type could be defined by:
(define-condition-type &i/o-file-is-read-only &i/o-file-protection make-i/o-file-is-read-only-error i/o-file-is-read-only-error?)A condition of this type specifies that an operation tried to operate on a named read–only file under the assumption that it is writeable.
This condition type could be defined by:
(define-condition-type &i/o-file-already-exists &i/o-filename make-i/o-file-already-exists-error i/o-file-already-exists-error?)A condition of this type specifies that an operation tried to operate on an existing named file under the assumption that it did not exist.
This condition type could be defined by:
(define-condition-type &i/o-file-does-not-exist &i/o-filename make-i/o-file-does-not-exist-error i/o-file-does-not-exist-error?)A condition of this type specifies that an operation tried to operate on an non–existent named file under the assumption that it existed.
This condition type could be defined by:
(define-condition-type &i/o-port &i/o make-i/o-port-error i/o-port-error? (port i/o-error-port))This condition type specifies the port with which an I/O error is associated. port should be the port. Conditions raised by procedures accepting a port as an argument should include an
&i/o-port-errorcondition.
The (rnrs io ports (6)) library defines an I/O layer for
conventional, imperative buffered input and output. A port
represents a buffered access object for a data sink or source or both
simultaneously. The library allows ports to be created from arbitrary
data sources and sinks.
The (rnrs io ports (6)) library distinguishes between input
ports and output ports. An input port is a source for data,
whereas an output port is a sink for data. A port may be both an input
port and an output port; such a port typically provides simultaneous
read and write access to a file or other data.
The (rnrs io ports (6)) library also distinguishes between
binary ports, which are sources or sinks for uninterpreted bytes,
and textual ports, which are sources or sinks for characters and
strings.
This section uses input-port, output-port, binary-port, textual-port, binary-input-port, textual-input-port, binary-output-port, textual-output-port, and port as names for arguments that must be input ports (or combined input/output ports), output ports (or combined input/output ports), binary ports, textual ports, binary input ports, textual input ports, binary output ports, textual output ports, or any kind of port, respectively.
Some of the procedures described in this chapter accept a file name as an argument. Valid values for such a file name include strings that name a file using the native notation of filesystem paths on an implementation's underlying operating system, and may include implementation–dependent values as well.
A filename parameter name means that the corresponding argument must be a file name.
When opening a file, the various procedures in this library accept a
file-options object that encapsulates flags to specify how the
file is to be opened. A file-options object is an enum–set over
the symbols constituting valid file options. A file-options
parameter name means that the corresponding argument must be a
file–options object.
Each <file-options symbol> must be a symbol. The
file-optionssyntax returns a file–options object that encapsulates the specified options.When supplied to an operation that opens a file for output, the file–options object returned by
(file-options)(without arguments) specifies that the file is created if it does not exist and an exception with condition type&i/o-file-already-existsis raised if it does exist. The following standard options can be included to modify the default behavior.
no-create- If the file does not already exist, it is not created; instead, an exception with condition type
&i/o-file-does-not-existis raised. If the file already exists, the exception with condition type&i/o-file-already-existsis not raised and the file is truncated to zero length.no-fail- If the file already exists, the exception with condition type
&i/o-file-already-existsis not raised, even ifno-createis not included, and the file is truncated to zero length.no-truncate- If the file already exists and the exception with condition type
&i/o-file-already-existshas been inhibited by inclusion ofno-createorno-fail, the file is not truncated, but the port's current position is still set to the beginning of the file.These options have no effect when a file is opened only for input. Symbols other than those listed above may be used as <file-options symbol>s; they have implementation–specific meaning, if any.
NOTE Only the name of <file-options symbol> is significant.
Each port has an associated buffer mode. For an output port, the buffer mode defines when an output operation flushes the buffer associated with the output port. For an input port, the buffer mode defines how much data will be read to satisfy read operations. The possible buffer modes are the symbols:
nonelineblockThis section uses the parameter name buffer-mode for arguments that must be buffer-mode symbols.
If two ports are connected to the same mutable source, both ports are unbuffered, and reading a byte or character from that shared source via one of the two ports would change the bytes or characters seen via the other port, a lookahead operation on one port will render the peeked byte or character inaccessible via the other port, while a subsequent read operation on the peeked port will see the peeked byte or character even though the port is otherwise unbuffered.
In other words, the semantics of buffering is defined in terms of side effects on shared mutable sources, and a lookahead operation has the same side effect on the shared source as a read operation.
<buffer-mode symbol> must be a symbol whose name is one of
none,line, andblock. The result is the corresponding symbol, and specifies the associated buffer mode.NOTE Only the name of <buffer-mode symbol> is significant.
Return
#tif the argument is a valid buffer–mode symbol, and returns#fotherwise.
Several different Unicode encoding schemes describe standard ways to encode characters and strings as byte sequences and to decode those sequences. Within this document, a codec is an immutable Scheme object that represents a Unicode or similar encoding scheme.
An end–of–line style is a symbol that, if it is not
none, describes how a textual port transcodes representations of
line endings.
A transcoder is an immutable Scheme object that combines a codec with an end–of–line style and a method for handling decoding errors. Each transcoder represents some specific bidirectional (but not necessarily lossless), possibly stateful translation between byte sequences and Unicode characters and strings. Every transcoder can operate in the input direction (bytes to characters) or in the output direction (characters to bytes). A transcoder parameter name means that the corresponding argument must be a transcoder.
A binary port is a port that supports binary I/O, does not have an associated transcoder and does not support textual I/O. A textual port is a port that supports textual I/O, and does not support binary I/O. A textual port may or may not have an associated transcoder.
These are predefined codecs for the ISO 8859-1, UTF-8, and UTF-16 encoding schemes.
A call to any of these procedures returns a value that is equal in the sense of
eqv?to the result of any other call to the same procedure.
<eol-style symbol> should be a symbol whose name is one of
lf,cr,crlf,nel,crnel,ls, andnone.The form evaluates to the corresponding symbol. If the name of <eol-style symbol> is not one of these symbols, the effect and result are implementation–dependent; in particular, the result may be an eol–style symbol acceptable as an eol-style argument to
make-transcoder. Otherwise, an exception is raised.All eol–style symbols except
nonedescribe a specific line–ending encoding:lf <linefeed> cr <carriage return> crlf <carriage return> <linefeed> nel <next line> crnel <carriage return> <next line> ls <line separator>For a textual port with a transcoder, and whose transcoder has an eol–style symbol
none, no conversion occurs. For a textual input port, any eol–style symbol other thannonemeans that all of the above line-ending encodings are recognized and are translated into a single linefeed. For a textual output port,noneandlfare equivalent. Linefeed characters are encoded according to the specified eol-style symbol, and all other characters that participate in possible line endings are encoded as is.NOTE Only the name of <eol-style symbol> is significant.
Return the default end–of–line style of the underlying platform, e.g.,
lfon Unix andcrlfon Windows.
This condition type could be defined by:
(define-condition-type &i/o-decoding &i/o-port make-i/o-decoding-error i/o-decoding-error?)An exception with this type is raised when one of the operations for textual input from a port encounters a sequence of bytes that cannot be translated into a character or string by the input direction of the port's transcoder.
When such an exception is raised, the port's position is past the invalid encoding.
This condition type could be defined by:
(define-condition-type &i/o-encoding &i/o-port make-i/o-encoding-error i/o-encoding-error? (char i/o-encoding-error-char))An exception with this type is raised when one of the operations for textual output to a port encounters a character that cannot be translated into bytes by the output direction of the port's transcoder. cobj is the character that could not be encoded.
<error-handling-mode symbol> should be a symbol whose name is one of
ignore,raise, andreplace.The form evaluates to the corresponding symbol. If <error-handling-mode symbol> is not one of these identifiers, effect and result are implementation–dependent: The result may be an error–handling–mode symbol acceptable as a handling-mode argument to
make-transcoder. If it is not acceptable as a handling-mode argument tomake-transcoder, an exception is raised.NOTE Only the name of <error-handling-style symbol> is significant.The error–handling mode of a transcoder specifies the behavior of textual I/O operations in the presence of encoding or decoding errors.
If a textual input operation encounters an invalid or incomplete character encoding, and the error–handling mode is
ignore, an appropriate number of bytes of the invalid encoding are ignored and decoding continues with the following bytes. If the error–handling mode isreplace, the replacement characterU+FFFDis injected into the data stream, an appropriate number of bytes are ignored, and decoding continues with the following bytes. If the error–handling mode israise, an exception with condition type&i/o-decodingis raised.If a textual output operation encounters a character it cannot encode, and the error–handling mode is
ignore, the character is ignored and encoding continues with the next character. If the error–handling mode isreplace, a codec–specific replacement character is emitted by the transcoder, and encoding continues with the next character. The replacement character isU+FFFDfor transcoders whose codec is one of the Unicode encodings, but is the?character for the Latin–1 encoding. If the error–handling mode israise, an exception with condition type&i/o-encodingis raised.
codec must be a codec; eol-style, if present, an eol–style symbol; and handling-mode, if present, an error–handling–mode symbol.
eol-style may be omitted, in which case it defaults to the native end–of–line style of the underlying platform. handling-mode may be omitted, in which case it defaults to
replace. The result is a transcoder with the behavior specified by its arguments.
Return an implementation–dependent transcoder that represents a possibly locale-dependent “native” transcoding.
These are accessors for transcoder objects; when applied to a transcoder returned by
make-transcoder, they return the codec, eol-style, and handling-mode arguments, respectively.
Return the string that results from transcoding the bytevector according to the input direction of the transcoder.
Return the bytevector that results from transcoding the string according to the output direction of the transcoder.
The end–of–file object is returned by various I/O procedures when they reach end of file.
Return the end–of–file object.
(eqv? (eof-object) (eof-object)) ⇒ #t (eq? (eof-object) (eof-object)) ⇒ #t
NOTE The end–of–file object is not a datum value, and thus has no external representation.
The operations described in this section are common to input and output ports, both binary and textual. A port may also have an associated position that specifies a particular place within its data sink or source, and may also provide operations for inspecting and setting that place.
Return the transcoder associated with port if port is textual and has an associated transcoder, and returns
#fif port is binary or does not have an associated transcoder.
The
textual-port?procedure returns#tif port is textual, and returns#fotherwise.The
binary-port?procedure returns#tif port is binary, and returns#fotherwise.
The
transcoded-portprocedure returns a new textual port with the specified transcoder. Otherwise the new textual port's state is largely the same as that of binary-port.If binary-port is an input port, the new textual port will be an input port and will transcode the bytes that have not yet been read from binary-port. If binary-port is an output port, the new textual port will be an output port and will transcode output characters into bytes that are written to the byte sink represented by binary-port.
As a side effect, however,
transcoded-portcloses binary-port in a special way that allows the new textual port to continue to use the byte source or sink represented by binary-port, even though binary-port itself is closed and cannot be used by the input and output operations described in this chapter.
The
port-has-port-position?procedure returns#tif the port supports theport-positionoperation, and#fotherwise.For a binary port, the
port-positionprocedure returns the index of the position at which the next byte would be read from or written to the port as an exact non–negative integer object. For a textual port,port-positionreturns a value of some implementation–dependent type representing the port's position; this value may be useful only as the pos argument toset-port-position!, if the latter is supported on the port (see below).If the port does not support the operation,
port-positionraises an exception with condition type&assertion.NOTE For a textual port, the port position may or may not be an integer object. If it is an integer object, the integer object does not necessarily correspond to a byte or character position.
If port is a binary port, pos should be a non–negative exact integer object. If port is a textual port, pos should be the return value of a call to
port-positionon port.The
port-has-set-port-position!?procedure returns#tif the port supports theset-port-position!operation, and#fotherwise.The
set-port-position!procedure raises an exception with condition type&assertionif the port does not support the operation, and an exception with condition type&i/o-invalid-positionif pos is not in the range of valid positions of port. Otherwise, it sets the current position of the port to pos. If port is an output port,set-port-position!first flushes port.If port is a binary output port and the current position is set beyond the current end of the data in the underlying data sink, the object is not extended until new data is written at that position. The contents of any intervening positions are unspecified. Binary ports created by
open-file-output-portandopen-file-input/output-portcan always be extended in this manner within the limits of the underlying operating system. In other cases, attempts to set the port beyond the current end of data in the underlying object may result in an exception with condition type&i/o-invalid-position.
Closes the port, rendering the port incapable of delivering or accepting data. If port is an output port, it is flushed before being closed. This has no effect if the port has already been closed. A closed port is still a port. The
close-portprocedure returns unspecified values.
proc must accept one argument. The
call-with-portprocedure calls proc with port as an argument. If proc returns, port is closed automatically and the values returned by proc are returned. If proc does not return, port is not closed automatically, except perhaps when it is possible to prove that port will never again be used for an input or output operation.
An input port allows the reading of an infinite sequence of bytes or characters punctuated by end–of–file objects. An input port connected to a finite data source ends in an infinite sequence of end–of–file objects.
It is unspecified whether a character encoding consisting of several
bytes may have an end of file between the bytes. If, for example,
get-char raises an &i/o-decoding exception because the
character encoding at the port's position is incomplete up to the next
end of file, a subsequent call to get-char may successfully
decode a character if bytes completing the encoding are available after
the end of file.
Return
#tif the argument is an input port (or a combined input and output port), and returns#fotherwise.
Return
#tif thelookahead-u8procedure (if input-port is a binary port) or thelookahead-charprocedure (if input-port is a textual port) would return the end–of–file object, and#fotherwise. The operation may block indefinitely if no data is available but the port cannot be determined to be at end of file.
maybe-transcoder must be either a transcoder or
#f.The
open-file-input-portprocedure returns an input port for the named file. The file-options and maybe-transcoder arguments are optional.The file-options argument, which may determine various aspects of the returned port, defaults to the value of
(file-options).The buffer-mode argument, if supplied, must be one of the symbols that name a buffer mode. The buffer-mode argument defaults to
block.If maybe-transcoder is a transcoder, it becomes the transcoder associated with the returned port.
If maybe-transcoder is
#for absent, the port will be a binary port and will support theport-positionandset-port-position!operations. Otherwise the port will be a textual port, and whether it supports theport-positionandset-port-position!operations is implementation-dependent (and possibly transcoder-dependent).
maybe-transcoder must be either a transcoder or
#f.The
open-bytevector-input-portprocedure returns an input port whose bytes are drawn from bytevector. If transcoder is specified, it becomes the transcoder associated with the returned port.If maybe-transcoder is
#for absent, the port will be a binary port and will support theport-positionandset-port-position!operations. Otherwise the port will be a textual port, and whether it supports theport-positionandset-port-position!operations will be implementation–dependent (and possibly transcoder–dependent).If bytevector is modified after
open-bytevector-input-porthas been called, the effect on the returned port is unspecified.
Return a textual input port whose characters are drawn from string. The port may or may not have an associated transcoder; if it does, the transcoder is implementation–dependent. The port should support the
port-positionandset-port-position!operations.If string is modified after
open-string-input-porthas been called, the effect on the returned port is unspecified.
Return a fresh binary input port connected to standard input. Whether the port supports the
port-positionandset-port-position!operations is implementation–dependent.
This returns a default textual port for input. Normally, this default port is associated with standard input, but can be dynamically re–assigned using the
with-input-from-fileprocedure from the(rnrs io simple (6))library. The port may or may not have an associated transcoder; if it does, the transcoder is implementation–dependent.
Return a newly created binary input port whose byte source is an arbitrary algorithm represented by the
read!procedure. id must be a string naming the new port, provided for informational purposes only. read! must be a procedure and should behave as specified below; it will be called by operations that perform binary input.Each of the remaining arguments may be
#f; if any of those arguments is not#f, it must be a procedure and should behave as specified below.
(read! bytevector start count)- start will be a non–negative exact integer object, count will be a positive exact integer object, and bytevector will be a bytevector whose length is at least start+count.
The read! procedure should obtain up to count bytes from the byte source, and should write those bytes into bytevector starting at index start. The read! procedure should return an exact integer object. This integer object should represent the number of bytes that it has read. To indicate an end of file, the read! procedure should write no bytes and return 0.
(get-position)- The get-position procedure (if supplied) should return an exact integer object representing the current position of the input port. If not supplied, the custom port will not support the
port-positionoperation.(set-position! pos)- pos will be a non–negative exact integer object. The set-position! procedure (if supplied) should set the position of the input port to pos. If not supplied, the custom port will not support the
set-port-position!operation.(close)- The close procedure (if supplied) should perform any actions that are necessary when the input port is closed.
Implementation responsibilities: The implementation must check the return values of read! and get-position only when it actually calls them as part of an I/O operation requested by the program. The implementation is not required to check that these procedures otherwise behave as described. If they do not, however, the behavior of the resulting port is unspecified.
Return a newly created textual input port whose character source is an arbitrary algorithm represented by the read! procedure. id must be a string naming the new port, provided for informational purposes only. read! must be a procedure and should behave as specified below; it will be called by operations that perform textual input.
Each of the remaining arguments may be
#f; if any of those arguments is not#f, it must be a procedure and should behave as specified below.
(read! string start count)- start will be a non–negative exact integer object, count will be a positive exact integer object, and string will be a string whose length is at least start+count.
The read! procedure should obtain up to count characters from the character source, and should write those characters into string starting at index start. The read! procedure should return an exact integer object representing the number of characters that it has written. To indicate an end of file, the read! procedure should write no bytes and return 0.
(get-position)- The get-position procedure (if supplied) should return a single value. The return value should represent the current position of the input port. If not supplied, the custom port will not support the
port-positionoperation.(set-position! pos)- The set-position! procedure (if supplied) should set the position of the input port to pos if pos is the return value of a call to get-position. If not supplied, the custom port will not support the
set-port-position!operation.(close)- The close procedure (if supplied) should perform any actions that are necessary when the input port is closed.
The port may or may not have an an associated transcoder; if it does, the transcoder is implementation–dependent.
Implementation responsibilities: The implementation must check the return values of read! and get-position only when it actually calls them as part of an I/O operation requested by the program. The implementation is not required to check that these procedures otherwise behave as described. If they do not, however, the behavior of the resulting port is unspecified.
Read from binary-input-port, blocking as necessary, until a byte is available from binary-input-port or until an end of file is reached. If a byte becomes available,
get-u8returns the byte as an octet and updates binary-input-port to point just past that byte. If no input byte is seen before an end of file is reached, the end–of–file object is returned.
The
lookahead-u8procedure is likeget-u8, but it does not update binary-input-port to point past the byte.
count must be an exact, non–negative integer object representing the number of bytes to be read.
The
get-bytevector-nprocedure reads from binary-input-port, blocking as necessary, until count bytes are available from binary-input-port or until an end of file is reached.If count bytes are available before an end of file,
get-bytevector-nreturns a bytevector of size count.If fewer bytes are available before an end of file,
get-bytevector-nreturns a bytevector containing those bytes. In either case, the input port is updated to point just past the bytes read.If an end of file is reached before any bytes are available,
get-bytevector-nreturns the end–of–file object.
count must be an exact, non–negative integer object, representing the number of bytes to be read. bytevector must be a bytevector with at least start+count elements.
The
get-bytevector-n!procedure reads from binary-input-port, blocking as necessary, until count bytes are available from binary-input-port or until an end of file is reached.If count bytes are available before an end of file, they are written into bytevector starting at index start, and the result is count.
If fewer bytes are available before the next end of file, the available bytes are written into bytevector starting at index start, and the result is a number object representing the number of bytes actually read.
In either case, the input port is updated to point just past the bytes read. If an end of file is reached before any bytes are available,
get-bytevector-n!returns the end–of–file object.
Read from binary-input-port, blocking as necessary, until bytes are available from binary-input-port or until an end of file is reached. If bytes become available,
get-bytevector-somereturns a freshly allocated bytevector containing the initial available bytes (at least one), and it updates binary-input-port to point just past these bytes. If no input bytes are seen before an end of file is reached, the end–of–file object is returned.
Attempts to read all bytes until the next end of file, blocking as necessary. If one or more bytes are read,
get-bytevector-allreturns a bytevector containing all bytes up to the next end of file. Otherwise,get-bytevector-allreturns the end–of–file object. The operation may block indefinitely waiting to see if more bytes will become available, even if some bytes are already available.
Read from textual-input-port, blocking as necessary, until a complete character is available from textual-input-port, or until an end of file is reached.
If a complete character is available before the next end of file,
get-charreturns that character and updates the input port to point past the character. If an end of file is reached before any character is read,get-charreturns the end–of–file object.
The
lookahead-charprocedure is likeget-char, but it does not update textual-input-port to point past the character.NOTE With some of the standard transcoders described in this document, up to four bytes of lookahead are needed. Non–standard transcoders may need even more lookahead.
count must be an exact, non–negative integer object, representing the number of characters to be read.
The
get-string-nprocedure reads from textual-input-port, blocking as necessary, until count characters are available, or until an end of file is reached.If count characters are available before end of file,
get-string-nreturns a string consisting of those count characters.If fewer characters are available before an end of file, but one or more characters can be read,
get-string-nreturns a string containing those characters.In either case, the input port is updated to point just past the characters read. If no characters can be read before an end of file, the end-of-file object is returned.
start and count must be exact, non–negative integer objects, with count representing the number of characters to be read. string must be a string with at least start+count characters.
The
get-string-n!procedure reads from textual-input-port in the same manner asget-string-n.If count characters are available before an end of file, they are written into string starting at index start, and count is returned.
If fewer characters are available before an end of file, but one or more can be read, those characters are written into string starting at index start and the number of characters actually read is returned as an exact integer object.
If no characters can be read before an end of file, the end–of–file object is returned.
Read from textual-input-port until an end of file, decoding characters in the same manner as
get-string-nandget-string-n!.If characters are available before the end of file, a string containing all the characters decoded from that data are returned. If no character precedes the end of file, the end–of–file object is returned.
Read from textual-input-port up to and including the linefeed character or end of file, decoding characters in the same manner as
get-string-nandget-string-n!.If a linefeed character is read, a string containing all of the text up to (but not including) the linefeed character is returned, and the port is updated to point just past the linefeed character.
If an end of file is encountered before any linefeed character is read, but some characters have been read and decoded as characters, a string containing those characters is returned.
If an end of file is encountered before any characters are read, the end–of–file object is returned.
NOTE The end–of–line style, if notnone, will cause all line endings to be read as linefeed characters.
Read an external representation from textual-input-port and returns the datum it represents. The
get-datumprocedure returns the next datum that can be parsed from the given textual-input-port, updating textual-input-port to point exactly past the end of the external representation of the object.Any <interlexeme space> (lang lex syntax) in the input is first skipped. If an end of file occurs after the <interlexeme space>, the end–of–file object is returned.
If a character inconsistent with an external representation is encountered in the input, an exception with condition types
&lexicaland&i/o-readis raised.Also, if the end of file is encountered after the beginning of an external representation, but the external representation is incomplete and therefore cannot be parsed, an exception with condition types
&lexicaland&i/o-readis raised.
An output port is a sink to which bytes or characters are written. The written data may control external devices or may produce files and other objects that may subsequently be opened for input.
Return
#tif the argument is an output port (or a combined input and output port),#fotherwise.
Flushe any buffered output from the buffer of output-port to the underlying file, device, or object. The
flush-output-portprocedure returns unspecified values.
Return the symbol that represents the buffer mode of output-port.
maybe-transcoder must be either a transcoder or
#f.The
open-file-output-portprocedure returns an output port for the named file.The file-options argument, which may determine various aspects of the returned port, defaults to the value of
(file-options).The buffer-mode argument, if supplied, must be one of the symbols that name a buffer mode. The buffer-mode argument defaults to
block.If maybe-transcoder is a transcoder, it becomes the transcoder associated with the port.
If maybe-transcoder is
#for absent, the port will be a binary port and will support theport-positionandset-port-position!operations. Otherwise the port will be a textual port, and whether it supports theport-positionandset-port-position!operations is implementation–dependent (and possibly transcoder–dependent).
maybe-transcoder must be either a transcoder or
#f.The
open-bytevector-output-portprocedure returns two values: an output port and an extraction procedure. The output port accumulates the bytes written to it for later extraction by the procedure.If maybe-transcoder is a transcoder, it becomes the transcoder associated with the port. If maybe-transcoder is
#for absent, the port will be a binary port and will support theport-positionandset-port-position!operations. Otherwise the port will be a textual port, and whether it supports theport-positionandset-port-position!operations is implementation–dependent (and possibly transcoder–dependent).The extraction procedure takes no arguments. When called, it returns a bytevector consisting of all the port's accumulated bytes (regardless of the port's current position), removes the accumulated bytes from the port, and resets the port's position.
proc must accept one argument. maybe-transcoder must be either a transcoder or
#f.The
call-with-bytevector-output-portprocedure creates an output port that accumulates the bytes written to it and calls proc with that output port as an argument.Whenever proc returns, a bytevector consisting of all of the port's accumulated bytes (regardless of the port's current position) is returned and the port is closed.
The transcoder associated with the output port is determined as for a call to
open-bytevector-output-port.
Return two values: a textual output port and an extraction procedure. The output port accumulates the characters written to it for later extraction by the procedure.
The port may or may not have an associated transcoder; if it does, the transcoder is implementation-dependent. The port should support the
port-positionandset-port-position!operations.The extraction procedure takes no arguments. When called, it returns a string consisting of all of the port's accumulated characters (regardless of the current position), removes the accumulated characters from the port, and resets the port's position.
proc must accept one argument.
The
call-with-string-output-portprocedure creates a textual output port that accumulates the characters written to it and calls proc with that output port as an argument.Whenever proc returns, a string consisting of all of the port's accumulated characters (regardless of the port's current position) is returned and the port is closed.
The port may or may not have an associated transcoder; if it does, the transcoder is implementation-dependent. The port should support the
port-positionandset-port-position!operations.
Return a fresh binary output port connected to the standard output or standard error respectively. Whether the port supports the
port-positionandset-port-position!operations is implementation–dependent.
These return default textual ports for regular output and error output. Normally, these default ports are associated with standard output, and standard error, respectively.
The return value of
current-output-portcan be dynamically re–assigned using thewith-output-to-fileprocedure from the(rnrs io simple (6))library. A port returned by one of these procedures may or may not have an associated transcoder; if it does, the transcoder is implementation–dependent.
Return a newly created binary output port whose byte sink is an arbitrary algorithm represented by the write! procedure. id must be a string naming the new port, provided for informational purposes only. write! must be a procedure and should behave as specified below; it will be called by operations that perform binary output.
Each of the remaining arguments may be
#f; if any of those arguments is not#f, it must be a procedure and should behave as specified in the description ofmake-custom-binary-input-port.
(write! bytevector start count)- start and count will be non-negative exact integer objects, and bytevector will be a bytevector whose length is at least start+count.
The write! procedure should write up to count bytes from bytevector starting at index start to the byte sink. In any case, the write! procedure should return the number of bytes that it wrote, as an exact integer object.
Implementation responsibilities: The implementation must check the return values of write! only when it actually calls write! as part of an I/O operation requested by the program. The implementation is not required to check that write! otherwise behaves as described. If it does not, however, the behavior of the resulting port is unspecified.
Return a newly created textual output port whose byte sink is an arbitrary algorithm represented by the write! procedure. Id must be a string naming the new port, provided for informational purposes only. write! must be a procedure and should behave as specified below; it will be called by operations that perform textual output.
Each of the remaining arguments may be
#f; if any of those arguments is not#f, it must be a procedure and should behave as specified in the description ofmake-custom-textual-input-port.
(write! string start count)- start and count will be non–negative exact integer objects, and string will be a string whose length is at least start+count.
The write! procedure should write up to count characters from string starting at index start to the character sink. In any case, the write! procedure should return the number of characters that it wrote, as an exact integer object.
The port may or may not have an associated transcoder; if it does, the transcoder is implementation–dependent.
Implementation responsibilities: The implementation must check the return values of write! only when it actually calls write! as part of an I/O operation requested by the program. The implementation is not required to check that write! otherwise behaves as described. If it does not, however, the behavior of the resulting port is unspecified.
Write octet to the output port and returns unspecified values.
start and count must be non–negative exact integer objects that default to 0 and:
(- (bytevector-length bytevector) start)respectively. bytevector must have a length of at least start+count. The
put-bytevectorprocedure writes the count bytes of the bytevector bytevector starting at index start to the output port. Theput-bytevectorprocedure returns unspecified values.
Write char to the port. The
put-charprocedure returns unspecified values.
start and count must be non–negative exact integer objects. String must have a length of at least start+count.
start defaults to 0. count defaults to:
(- (string-length string) start)The
put-stringprocedure writes the count characters of string starting at index start to the port. Theput-stringprocedure returns unspecified values.
datum should be a datum value.
The
put-datumprocedure writes an external representation of datum to textual-output-port. The specific external representation is implementation–dependent. However, whenever possible, an implementation should produce a representation for whichget-datum, when reading the representation, will return an object equal (in the sense ofequal?) to datum.NOTE Not all datums may allow producing an external representation for whichget-datumwill produce an object that is equal to the original. Specifically, NaNs contained in datum may make this impossible.NOTE Theput-datumprocedure merely writes the external representation, but no trailing delimiter. Ifput-datumis used to write several subsequent external representations to an output port, care should be taken to delimit them properly so they can be read back in by subsequent calls toget-datum.
Return a single port that is both an input port and an output port for the named file. The optional arguments default as described in the specification of
open-file-output-port. If the input/output port supportsport-positionand/orset-port-position!, the same port position is used for both input and output.
Return a newly created binary input/output port whose byte source and sink are arbitrary algorithms represented by the read! and write! procedures.
id must be a string naming the new port, provided for informational purposes only.
read! and write! must be procedures, and should behave as specified for the
make-custom-binary-input-portandmake-custom-binary-output-portprocedures.Each of the remaining arguments may be
#f; if any of those arguments is not#f, it must be a procedure and should behave as specified in the description ofmake-custom-binary-input-port.
Return a newly created textual input/output port whose textual source and sink are arbitrary algorithms represented by the read! and write! procedures.
id must be a string naming the new port, provided for informational purposes only.
read! and write! must be procedures, and should behave as specified for the
make-custom-textual-input-portandmake-custom-textual-output-portprocedures.Each of the remaining arguments may be
#f; if any of those arguments is not#f, it must be a procedure and should behave as specified in the description ofmake-custom-textual-input-port.
This section describes the (rnrs io simple (6)) library, which
provides a somewhat more convenient interface for performing textual I/O
on ports. This library implements most of the I/O procedures of the
previous revision of this report.
The ports created by the procedures of this library are textual ports associated implementation–dependent transcoders.
These are the same as
eof-objectandeof-object?from the(rnrs ports (6))library.
proc should accept one argument.
These procedures open the file named by filename for input or for output, with no specified file options, and call proc with the obtained port as an argument.
If proc returns, the port is closed automatically and the values returned by proc are returned.
If proc does not return, the port is not closed automatically, unless it is possible to prove that the port will never again be used for an I/O operation.
These are the same as the
input-port?andoutput-port?procedures in the(rnrs io ports (6))library.
These are the same as the
current-input-port,current-output-port, andcurrent-error-portprocedures from the(rnrs io ports (6))library.
thunk must be a procedure and must accept zero arguments.
The file is opened for input or output using empty file options, and thunk is called with no arguments.
During the dynamic extent of the call to thunk, the obtained port is made the value returned by
current-input-portorcurrent-output-portprocedures; the previous default values are reinstated when the dynamic extent is exited.When thunk returns, the port is closed automatically. The values returned by thunk are returned.
If an escape procedure is used to escape back into the call to thunk after thunk is returned, the behavior is unspecified.
Open filename for input, with empty file options, and returns the obtained port.
Open filename for output, with empty file options, and returns the obtained port.
Close input-port or output-port, respectively.
Reads from textual-input-port, blocking as necessary until a character is available from textual-input-port, or the data that are available cannot be the prefix of any valid encoding, or an end of file is reached.
If a complete character is available before the next end of file,
read-charreturns that character, and updates the input port to point past that character.If an end of file is reached before any data are read,
read-charreturns the end–of–file object.If textual-input-port is omitted, it defaults to the value returned by
current-input-port.
This is the same as
read-char, but does not consume any data from the port.
Read an external representation from textual-input-port and returns the datum it represents.
The
readprocedure operates in the same way asget-datum.If textual-input-port is omitted, it defaults to the value returned by
current-input-port.
Write an encoding of the character char to the textual-output-port, and returns unspecified values.
If textual-output-port is omitted, it defaults to the value returned by
current-output-port.
This is equivalent to using
write-charto write#\linefeedto textual-output-port.If textual-output-port is omitted, it defaults to the value returned by
current-output-port.
Write a representation of obj to the given textual-output-port.
Strings that appear in the written representation are not enclosed in doublequotes, and no characters are escaped within those strings.
Character objects appear in the representation as if written by
write-charinstead of bywrite.The
displayprocedure returns unspecified values.The textual-output-port argument may be omitted, in which case it defaults to the value returned by
current-output-port.
Write the external representation of obj to textual-output-port.
The
writeprocedure operates in the same way asput-datum.If textual-output-port is omitted, it defaults to the value returned by
current-output-port.
This chapter describes the (rnrs files (6)) library for operations on
the file system. This library, in addition to the procedures described
here, also exports the I/O condition types described in section
stdlib io conditions.
filename must be a file name.
The
file-exists?procedure returns#tif the named file exists at the time the procedure is called,#fotherwise.
filename must be a file name.
The
delete-fileprocedure deletes the named file if it exists and can be deleted, and returns unspecified values. If the file does not exist or cannot be deleted, an exception with condition type&i/o-filenameis raised.
The procedures described in this section are exported by the
(rnrs programs (6)) library.
Return a nonempty list of strings. The first element is an implementation–specific name for the running top–level program. The remaining elements are command–line arguments according to the operating system's conventions.
Exit the running program and communicates an exit value to the operating system.
If no argument is supplied, the
exitprocedure should communicate to the operating system that the program exited normally.If an argument is supplied, the
exitprocedure should translate the argument into an appropriate exit value for the operating system. If obj is#f, the exit is assumed to be abnormal.
This chapter describes Scheme's libraries for more specialized numerical operations: fixnum and flonum arithmetic, as well as bitwise operations on exact integer objects.
A number of procedures operate on the binary two's–complement representations of exact integer objects: Bit positions within an exact integer object are counted from the right, i.e. bit 0 is the least significant bit. Some procedures allow extracting bit fields, i.e., number objects representing subsequences of the binary representation of an exact integer object. Bit fields are always positive, and always defined using a finite number of bits.
Every implementation must define its fixnum range as a closed interval: [-2^(w-1), 2^(w-1)-1] such that w is a (mathematical) integer w >= 24. Every mathematical integer within an implementation's fixnum range must correspond to an exact integer object that is representable within the implementation. A fixnum is an exact integer object whose value lies within this fixnum range.
This section describes the (rnrs arithmetic fixnums (6)) library,
which defines various operations on fixnums. Fixnum operations perform
integer arithmetic on their fixnum arguments, but raise an exception
with condition type &implementation-restriction if the result is
not a fixnum.
This section uses fx, fx1, fx2, etc., as names for arguments that must be fixnums.
Return
#tif obj is an exact integer object within the fixnum range,#fotherwise.
These procedures return w, -2^(w-1) and 2^(w-1)-1: the width, minimum and the maximum value of the fixnum range, respectively.
These procedures return
#tif their arguments are (respectively): equal, monotonically increasing, monotonically decreasing, monotonically nondecreasing, or monotonically nonincreasing,#fotherwise.
These numerical predicates test a fixnum for a particular property, returning
#tor#f. The five properties tested by these procedures are: whether the number object is zero, greater than zero, less than zero, odd, or even.
These procedures return the maximum or minimum of their arguments.
These procedures return the sum or product of their arguments, provided that sum or product is a fixnum. An exception with condition type
&implementation-restrictionis raised if that sum or product is not a fixnum.
With two arguments, this procedure returns the difference fx1 - fx2, provided that difference is a fixnum.
With one argument, this procedure returns the additive inverse of its argument, provided that integer object is a fixnum.
An exception with condition type
&implementation-restrictionis raised if the mathematically correct result of this procedure is not a fixnum.(fx- (least-fixnum)) ⇒ exception &assertion
fx2 must be nonzero.
These procedures implement number–theoretic integer division and return the results of the corresponding mathematical operations specified in Integer division
(fxdiv fx1 fx2) ⇒ fx1 div fx2 (fxmod fx1 fx2) ⇒ fx1 mod fx2 (fxdiv-and-mod fx1 fx2) ⇒ fx1 div fx2, fx1 mod fx2 ; two return values (fxdiv0 fx1 fx2) ⇒ fx1 div_0 fx2 (fxmod0 fx1 fx2) ⇒ fx1 mod_0 fx2 (fxdiv0-and-mod0 fx1 fx2) ⇒ fx1 fx1 div_0 fx2, fx1 mod_0 fx2 ; two return values
Return the two fixnum results of the following computation:
(let* ((s (+ fx1 fx2 fx3)) (s0 (mod0 s (expt 2 (fixnum-width)))) (s1 (div0 s (expt 2 (fixnum-width))))) (values s0 s1))
Return the two fixnum results of the following computation:
(let* ((d (- fx1 fx2 fx3)) (d0 (mod0 d (expt 2 (fixnum-width)))) (d1 (div0 d (expt 2 (fixnum-width))))) (values d0 d1))
Return the two fixnum results of the following computation:
(let* ((s (+ (* fx1 fx2) fx3)) (s0 (mod0 s (expt 2 (fixnum-width)))) (s1 (div0 s (expt 2 (fixnum-width))))) (values s0 s1))
Return the unique fixnum that is congruent mod 2^w to the one's–complement of fx.
These procedures return the fixnum that is the bit–wise “and”, “inclusive or”, or “exclusive or” of the two's complement representations of their arguments. If they are passed only one argument, they return that argument. If they are passed no arguments, they return the fixnum (either -1 or 0) that acts as identity for the operation.
Return the fixnum that is the bit–wise “if” of the two's complement representations of its arguments, i.e. for each bit, if it is 1 in fx1, the corresponding bit in fx2 becomes the value of the corresponding bit in the result, and if it is 0, the corresponding bit in fx3 becomes the corresponding bit in the value of the result. This is the fixnum result of the following computation:
(fxior (fxand fx1 fx2) (fxand (fxnot fx1) fx3))
If fx is non–negative, this procedure returns the number of 1 bits in the two's complement representation of fx. Otherwise it returns the result of the following computation:
(fxnot (fxbit-count (fxnot ei)))
Return the number of bits needed to represent fx if it is positive, and the number of bits needed to represent
(fxnotfx)if it is negative, which is the fixnum result of the following computation:(do ((result 0 (+ result 1)) (bits (if (fxnegative? fx) (fxnot fx) fx) (fxarithmetic-shift-right bits 1))) ((fxzero? bits) result))
Return the index of the least significant 1 bit in the two's complement representation of fx. If fx is 0, then -1 is returned.
(fxfirst-bit-set 0) ⇒ -1 (fxfirst-bit-set 1) ⇒ 0 (fxfirst-bit-set -4) ⇒ 2
fx2 must be non–negative and less than
(fixnum-width).The
fxbit-set?procedure returns#tif the fx2th bit is 1 in the two's complement representation of fx1, and#fotherwise. This is the fixnum result of the following computation:(not (fxzero? (fxand fx1 (fxarithmetic-shift-left 1 fx2))))
fx2 must be non–negative and less than
(fixnum-width). Fx3 must be 0 or 1.The
fxcopy-bitprocedure returns the result of replacing the fx2th bit of fx1 by fx3, which is the result of the following computation:(let* ((mask (fxarithmetic-shift-left 1 fx2))) (fxif mask (fxarithmetic-shift-left fx3 fx2) fx1))
fx2 and fx3 must be non-negative and less than
(fixnum-width). Moreover, fx2 must be less than or equal to fx3.The
fxbit-fieldprocedure returns the number represented by the bits at the positions from fx2 (inclusive) to fx3 (exclusive), which is the fixnum result of the following computation:(let* ((mask (fxnot (fxarithmetic-shift-left -1 fx3)))) (fxarithmetic-shift-right (fxand fx1 mask) fx2))
fx2 and fx3 must be non-negative and less than
(fixnum-width). Moreover, fx2 must be less than or equal to fx3.The
fxcopy-bit-fieldprocedure returns the result of replacing in fx1 the bits at positions from fx2 (inclusive) to fx3 (exclusive) by the bits in fx4 from position 0 (inclusive) to position fx3-fx2 (exclusive), which is the fixnum result of the following computation:(let* ((to fx1) (start fx2) (end fx3) (from fx4) (mask1 (fxarithmetic-shift-left -1 start)) (mask2 (fxnot (fxarithmetic-shift-left -1 end))) (mask (fxand mask1 mask2)) (mask3 (fxnot (fxarithmetic-shift-left -1 (- end start))))) (fxif mask (fxarithmetic-shift-left (fxand from mask3) start) to))
The absolute value of fx2 must be less than
(fixnum-width).If:
(floor (* fx1 (expt 2 fx2)))is a fixnum, then that fixnum is returned. Otherwise an exception with condition type
&implementation-restrictionis raised.
fx2 must be non–negative, and less than
(fixnum-width).The
fxarithmetic-shift-leftprocedure behaves the same asfxarithmetic-shift, and(fxarithmetic-shift-rightfx1 fx2)behaves the same as(fxarithmetic-shiftfx1(fx-fx2)).
fx2, fx3, and fx4 must be non–negative and less than
(fixnum-width). fx2 must be less than or equal to fx3. fx4 must be less than the difference between fx3 and fx2.The
fxrotate-bit-fieldprocedure returns the result of cyclically permuting in fx1 the bits at positions from fx2 (inclusive) to fx3 (exclusive) by fx4 bits towards the more significant bits, which is the result of the following computation:(let* ((n fx1) (start fx2) (end fx3) (count fx4) (width (fx- end start))) (if (fxpositive? width) (let* ((count (fxmod count width)) (field0 (fxbit-field n start end)) (field1 (fxarithmetic-shift-left field0 count)) (field2 (fxarithmetic-shift-right field0 (fx- width count))) (field (fxior field1 field2))) (fxcopy-bit-field n start end field)) n))
fx2 and fx3 must be non-negative and less than
(fixnum-width). Moreover, fx2 must be less than or equal to fx3.The
fxreverse-bit-fieldprocedure returns the fixnum obtained from fx1 by reversing the order of the bits at positions from fx2 (inclusive) to fx3 (exclusive).(fxreverse-bit-field #b1010010 1 4) ⇒ 88 ; #b1011000
This section describes the (rnrs arithmetic flonums (6)) library.
This section uses fl, fl1, fl2, etc., as parameter
names for arguments that must be flonums, and ifl as a name for
arguments that must be integer–valued flonums, i.e., flonums for which
the integer-valued? predicate returns true.
Return the best flonum representation of x.
The value returned is a flonum that is numerically closest to the argument.
NOTE If flonums are represented in binary floating point, then implementations should break ties by preferring the floating–point representation whose least significant bit is zero.
These procedures return
#tif their arguments are (respectively): equal, monotonically increasing, monotonically decreasing, monotonically nondecreasing, or monotonically nonincreasing,#fotherwise. These predicates must be transitive.(fl=? +inf.0 +inf.0) ⇒ #t (fl=? -inf.0 +inf.0) ⇒ #f (fl=? -inf.0 -inf.0) ⇒ #t (fl=? 0.0 -0.0) ⇒ #t (fl<? 0.0 -0.0) ⇒ #f (fl=? +nan.0 fl) ⇒ #f (fl<? +nan.0 fl) ⇒ #f
These numerical predicates test a flonum for a particular property, returning
#tor#f:
flinteger?- procedure tests whether the number object is an integer,
flzero?- tests whether it is
fl=?to zero,flpositive?- tests whether it is greater than zero,
flnegative?- tests whether it is less than zero,
flodd?- tests whether it is odd,
fleven?- tests whether it is even,
flfinite?- tests whether it is not an infinity and not a NaN,
flinfinite?- tests whether it is an infinity,
flnan?- tests whether it is a NaN.
(flnegative? -0.0) ⇒ #f (flfinite? +inf.0) ⇒ #f (flfinite? 5.0) ⇒ #t (flinfinite? 5.0) ⇒ #f (flinfinite? +inf.0) ⇒ #tNOTE(flnegative? -0.0)must return#f, else it would lose the correspondence with(fl< -0.0 0.0), which is#faccording to IEEE 754.
These procedures return the maximum or minimum of their arguments. They always return a NaN when one or more of the arguments is a NaN.
These procedures return the flonum sum or product of their flonum arguments. In general, they should return the flonum that best approximates the mathematical sum or product. (For implementations that represent flonums using IEEE binary floating point, the meaning of “best” is defined by the IEEE standards.)
(fl+ +inf.0 -inf.0) ⇒ +nan.0 (fl+ +nan.0 fl) ⇒ +nan.0 (fl* +nan.0 fl) ⇒ +nan.0
With two or more arguments, these procedures return the flonum difference or quotient of their flonum arguments, associating to the left.
With one argument, however, they return the additive or multiplicative flonum inverse of their argument.
In general, they should return the flonum that best approximates the mathematical difference or quotient. (For implementations that represent flonums using IEEE binary floating point, the meaning of “best” is reasonably well–defined by the IEEE standards.)
(fl- +inf.0 +inf.0) ⇒ +nan.0For undefined quotients,
fl/behaves as specified by the IEEE standards:(fl/ 1.0 0.0) ⇒ +inf.0 (fl/ -1.0 0.0) ⇒ -inf.0 (fl/ 0.0 0.0) ⇒ +nan.0
These procedures implement number–theoretic integer division and return the results of the corresponding mathematical operations specified in report section baselib math semantics integer.
In the cases where the mathematical requirements in baselib math semantics cannot be satisfied by any number object, either an exception is raised with condition type
&implementation-restriction, or unspecified flonums (one forfldiv,flmod,fldiv0andflmod0, two forfldiv-and-modandfldiv0-and-mod0) are returned.(fldiv fl1 fl2) ⇒ fl1 div fl2 (flmod fl1 fl2) ⇒ fl1 mod fl2 (fldiv-and-mod fl1 fl2) ⇒ fl1 div fl2, fl1 mod fl2 ; two return values (fldiv0 fl1 fl2) ⇒ fl1 div_0 fl2 (flmod0 fl1 fl2) ⇒ fl1 mod_0 fl2 (fldiv0-and-mod0 fl1 fl2) ⇒ fl1 div_0 fl2, fl1 mod_0 fl2 ; two return values
These procedures return the numerator or denominator of fl as a flonum; the result is computed as if fl was represented as a fraction in lowest terms. The denominator is always positive. The denominator of 0.0 is defined to be 1.0.
(flnumerator +inf.0) ⇒ +inf.0 (flnumerator -inf.0) ⇒ -inf.0 (fldenominator +inf.0) ⇒ 1.0 (fldenominator -inf.0) ⇒ 1.0 (flnumerator 0.75) ⇒ 3.0 ; probably (fldenominator 0.75) ⇒ 4.0 ; probablyImplementations should implement following behavior:
(flnumerator -0.0) ⇒ -0.0
These procedures return integral flonums for flonum arguments that are not infinities or NaNs.
flfloor- Returns the largest integral flonum not larger than fl.
flceiling- Returns the smallest integral flonum not smaller than fl.
fltruncate- Returns the integral flonum closest to fl whose absolute value is not larger than the absolute value of fl.
flround- Returns the closest integral flonum to fl, rounding to even when fl represents a number halfway between two integers.
Although infinities and NaNs are not integer objects, these procedures return an infinity when given an infinity as an argument, and a NaN when given a NaN:
(flfloor +inf.0) ⇒ +inf.0 (flceiling -inf.0) ⇒ -inf.0 (fltruncate +nan.0) ⇒ +nan.0
These procedures compute the usual transcendental functions.
flexp- Computes the base-E exponential of fl.
fllog- With a single argument computes the natural logarithm of fl (not the base ten logarithm);
(fllogfl1 fl2)computes the base–fl2 logarithm of fl1.flasinflacosflatan- Compute arcsine, arccosine, and arctangent, respectively.
(flatanfl1 fl2)computes the arc tangent of fl1/fl2.baselib math ops trascend for the underlying mathematical operations. In the event that these operations do not yield a real result for the given arguments, the result may be a NaN, or may be some unspecified flonum.
Implementations that use IEEE binary floating–point arithmetic should follow the relevant standards for these procedures.
(flexp +inf.0) ⇒ +inf.0 (flexp -inf.0) ⇒ 0.0 (fllog +inf.0) ⇒ +inf.0 (fllog 0.0) ⇒ -inf.0 (fllog -0.0) ⇒ unspecified ; if -0.0 is distinguished (fllog -inf.0) ⇒ +nan.0 (flatan -inf.0) ⇒ -1.5707963267948965 ; approximately (flatan +inf.0) ⇒ 1.5707963267948965 ; approximately
Returns the principal square root of fl. For -0.0,
flsqrtshould return -0.0; for other negative arguments, the result may be a NaN or some unspecified flonum.(flsqrt +inf.0) ⇒ +inf.0 (flsqrt -0.0) ⇒ -0.0
Either fl1 should be non–negative, or, if fl1 is negative, fl2 should be an integer object.
The
flexptprocedure returns fl1 raised to the power fl2. If fl1 is negative and fl2 is not an integer object, the result may be a NaN, or may be some unspecified flonum.If fl1 and fl2 are both zero, the result is 1.0. If fl1 is zero and fl2 is positive, the result is zero. If fl1 is negative, the result may be a NaN, or may be some unspecified flonum.
These condition types could be defined by the following code:
(define-condition-type &no-infinities &implementation-restriction make-no-infinities-violation no-infinities-violation?) (define-condition-type &no-nans &implementation-restriction make-no-nans-violation no-nans-violation?)These types describe that a program has executed an arithmetic operations that is specified to return an infinity or a NaN, respectively, on a Scheme implementation that is not able to represent the infinity or NaN. baselib math infinities.
Return a flonum that is numerically closest to fx.
NOTE The result of this procedure may not be numerically equal to fx, because the fixnum precision may be greater than the flonum precision.
This section describes the (rnrs arithmetic bitwise (6)) library.
The exact bitwise arithmetic provides generic operations on exact
integer objects. This section uses ei, ei1, ei2,
etc., as parameter names that must be exact integer objects.
Returns the exact integer object whose two's complement representation is the one's complement of the two's complement representation of ei.
These procedures return the exact integer object that is the bit–wise “and”, “inclusive or”, or “exclusive or” of the two's complement representations of their arguments. If they are passed only one argument, they return that argument. If they are passed no arguments, they return the integer object (either -1 or 0) that acts as identity for the operation.
Return the exact integer object that is the bit–wise “if” of the two's complement representations of its arguments, i.e. for each bit, if it is 1 in ei1, the corresponding bit in ei2 becomes the value of the corresponding bit in the result, and if it is 0, the corresponding bit in ei3 becomes the corresponding bit in the value of the result. This is the result of the following computation:
(bitwise-ior (bitwise-and ei1 ei2) (bitwise-and (bitwise-not ei1) ei3))
If ei is non–negative, this procedure returns the number of 1 bits in the two's complement representation of ei. Otherwise it returns the result of the following computation:
(bitwise-not (bitwise-bit-count (bitwise-not ei)))
Return the number of bits needed to represent ei if it is positive, and the number of bits needed to represent
(bitwise-notei)if it is negative, which is the exact integer object that is the result of the following computation:(do ((result 0 (+ result 1)) (bits (if (negative? ei) (bitwise-not ei) ei) (bitwise-arithmetic-shift bits -1))) ((zero? bits) result))
Returns the index of the least significant 1 bit in the two's complement representation of ei. If ei is 0, then -1 is returned.
(bitwise-first-bit-set 0) ⇒ -1 (bitwise-first-bit-set 1) ⇒ 0 (bitwise-first-bit-set -4) ⇒ 2
ei2 must be non–negative.
The
bitwise-bit-set?procedure returns#tif the ei2th bit is 1 in the two's complement representation of ei1, and#fotherwise. This is the result of the following computation:(not (zero? (bitwise-and (bitwise-arithmetic-shift-left 1 ei2) ei1)))
ei2 must be non–negative, and ei3 must be either 0 or 1.
The
bitwise-copy-bitprocedure returns the result of replacing the ei2th bit of ei1 by ei3, which is the result of the following computation:(let* ((mask (bitwise-arithmetic-shift-left 1 ei2))) (bitwise-if mask (bitwise-arithmetic-shift-left ei3 ei2) ei1))
ei2 and ei3 must be non–negative, and ei2 must be less than or equal to ei3.
The
bitwise-bit-fieldprocedure returns the number represented by the bits at the positions from ei2 (inclusive) to ei3 (exclusive), which is the result of the following computation:(let ((mask (bitwise-not (bitwise-arithmetic-shift-left -1 ei3)))) (bitwise-arithmetic-shift-right (bitwise-and ei1 mask) ei2))
ei2 and ei3 must be non–negative, and ei2 must be less than or equal to ei3.
The
bitwise-copy-bit-fieldprocedure returns the result of replacing in ei1 the bits at positions from ei2 (inclusive) to ei3 (exclusive) by the bits in ei4 from position 0 (inclusive) to position ei3 - ei2 (exclusive) which is the result of the following computation:(let* ((to ei1) (start ei2) (end ei3) (from ei4) (mask1 (bitwise-arithmetic-shift-left -1 start)) (mask2 (bitwise-not (bitwise-arithmetic-shift-left -1 end))) (mask (bitwise-and mask1 mask2))) (bitwise-if mask (bitwise-arithmetic-shift-left from start) to))
Return the result of the following computation:
(floor (* ei1 (expt 2 ei2)))Examples:
(bitwise-arithmetic-shift -6 -1) ⇒ -3 (bitwise-arithmetic-shift -5 -1) ⇒ -3 (bitwise-arithmetic-shift -4 -1) ⇒ -2 (bitwise-arithmetic-shift -3 -1) ⇒ -2 (bitwise-arithmetic-shift -2 -1) ⇒ -1 (bitwise-arithmetic-shift -1 -1) ⇒ -1
ei2 must be non–negative.
The
bitwise-arithmetic-shift-leftprocedure returns the same result asbitwise-arithmetic-shift, and:(bitwise-arithmetic-shift-right ei1 ei2)returns the same result as:
(bitwise-arithmetic-shift ei1 (- ei2))
ei2, ei3, ei4 must be non–negative, ei2 must be less than or equal to ei3, and ei4 must be non-negative.
The procedure returns the result of cyclically permuting in ei1 the bits at positions from ei2 (inclusive) to ei3 (exclusive) by ei4 bits towards the more significant bits, which is the result of the following computation:
(let* ((n ei1) (start ei2) (end ei3) (count ei4) (width (- end start))) (if (positive? width) (let* ((count (mod count width)) (field0 (bitwise-bit-field n start end)) (field1 (bitwise-arithmetic-shift-left field0 count)) (field2 (bitwise-arithmetic-shift-right field0 (- width count))) (field (bitwise-ior field1 field2))) (bitwise-copy-bit-field n start end field)) n))
ei2 and ei3 must be non–negative, and ei2 must be less than or equal to ei3.
The
bitwise-reverse-bit-fieldprocedure returns the result obtained from ei1 by reversing the order of the bits at positions from ei2 (inclusive) to ei3 (exclusive).(bitwise-reverse-bit-field #b1010010 1 4) ⇒ 88 ; #b1011000
The (rnrs syntax-case (6)) library provides support for writing
low–level macros in a high–level style, with automatic syntax
checking, input destructuring, output restructuring, maintenance of
lexical scoping and referential transparency (hygiene), and support for
controlled identifier capture.
Examples
Barendregt's hygiene condition for the lambda calculus is an informal notion that requires the free variables of an expression N that is to be substituted into another expression M not to be captured by bindings in M when such capture is not intended.
Kohlbecker, et al. propose a corresponding hygiene condition for macro expansion that applies in all situations where capturing is not explicit: “Generated identifiers that become binding instances in the completely expanded program must only bind variables that are generated at the same transcription step”. In the terminology of this document, the “generated identifiers” are those introduced by a transformer rather than those present in the form passed to the transformer, and a “macro transcription step” corresponds to a single call by the expander to a transformer. Also, the hygiene condition applies to all introduced bindings rather than to introduced variable bindings alone.
This leaves open what happens to an introduced identifier that appears
outside the scope of a binding introduced by the same call. Such an
identifier refers to the lexical binding in effect where it appears
(within a syntax <template>) inside the transformer body or
one of the helpers it calls. This is essentially the referential
transparency property described by Clinger and Rees. Thus, the hygiene
condition can be restated as follows:
A binding for an identifier introduced into the output of a transformer call from the expander must capture only references to the identifier introduced into the output of the same transformer call.A reference to an identifier introduced into the output of a transformer refers to the closest enclosing binding for the introduced identifier or, if it appears outside of any enclosing binding for the introduced identifier, the closest enclosing lexical binding where the identifier appears (within a
syntax<template>) inside the transformer body or one of the helpers it calls.
Explicit captures are handled via datum->syntax.
Operationally, the expander can maintain hygiene with the help of marks. Marks are applied selectively by the expander to the output of each transformer it invokes, and substitutions are applied to the portions of each binding form that are supposed to be within the scope of the bound identifiers. Marks are used to distinguish like–named identifiers that are introduced at different times (either present in the source or introduced into the output of a particular transformer call), and substitutions are used to map identifiers to their expand-time values.
Each time the expander encounters a macro use, it applies an antimark to the input form, invokes the associated transformer, then applies a fresh mark to the output. Marks and antimarks cancel, so the portions of the input that appear in the output are effectively left unmarked, while the portions of the output that are introduced are marked with the fresh mark.
Each time the expander encounters a binding form it creates a set of
substitutions, each mapping one of the (possibly marked) bound
identifiers to information about the binding. (For a lambda
expression, the expander might map each bound identifier to a
representation of the formal parameter in the output of the expander.
For a let-syntax form, the expander might map each bound
identifier to the associated transformer.) These substitutions are
applied to the portions of the input form in which the binding is
supposed to be visible.
Marks and substitutions together form a wrap that is layered on the form being processed by the expander and pushed down toward the leaves as necessary. A wrapped form is referred to as a wrapped syntax object. Ultimately, the wrap may rest on a leaf that represents an identifier, in which case the wrapped syntax object is also referred to as an identifier. An identifier contains a name along with the wrap. (Names are typically represented by symbols.)
When a substitution is created to map an identifier to an expand–time
value, the substitution records the name of the identifier and the set
of marks that have been applied to that identifier, along with the
associated expand–time value. The expander resolves identifier
references by looking for the latest matching substitution to be applied
to the identifier, i.e., the outermost substitution in the wrap whose
name and marks match the name and marks recorded in the substitution.
The name matches if it is the same name (if using symbols, then by
eq?), and the marks match if the marks recorded with the
substitution are the same as those that appear below the
substitution in the wrap, i.e., those that were applied before
the substitution. Marks applied after a substitution, i.e., appear over
the substitution in the wrap, are not relevant and are ignored.
An algebra that defines how marks and substitutions work more precisely is given in section 2.4 of Oscar Waddell's PhD thesis.
A syntax object is a representation of a Scheme form that contains contextual information about the form in addition to its structure. This contextual information is used by the expander to maintain lexical scoping and may also be used by an implementation to maintain source–object correlation.
A syntax object may be wrapped. It may also be unwrapped, fully or partially, i.e., consist of list and vector structure with wrapped syntax objects or nonsymbol values at the leaves. More formally, a syntax object is:
The distinction between the terms “syntax object” and “wrapped syntax object” is important. For example, when invoked by the expander, a transformer must accept a wrapped syntax object but may return any syntax object, including an unwrapped syntax object.
Syntax objects representing identifiers are always wrapped and are distinct from other types of values. Wrapped syntax objects that are not identifiers may or may not be distinct from other types of values.
In define-syntax, let-syntax, and letrec-syntax
forms, a binding for a syntactic keyword is an expression that evaluates
to a transformer.
A transformer is a transformation procedure or a variable transformer. A transformation procedure is a procedure that must accept one argument, a wrapped syntax object representing the input, and return a syntax object representing the output. The transformer is called by the expander whenever a reference to a keyword with which it has been associated is found. If the keyword appears in the car of a list–structured input form, the transformer receives the entire list–structured form, and its output replaces the entire form.
Except with variable transformers (see below), if the keyword is found
in any other definition or expression context, the transformer receives
a wrapped syntax object representing just the keyword reference, and its
output replaces just the reference. Except with variable transformers,
an exception with condition type ‘&syntax’ is raised if the keyword
appears on the left–hand side of a set! expression.
proc should accept one argument, a wrapped syntax object, and return a syntax object.
The
make-variable-transformerprocedure creates a variable transformer. A variable transformer is like an ordinary transformer except that, if a keyword associated with a variable transformer appears on the left–hand side of aset!expression, an exception is not raised. Instead, proc is called with a wrapped syntax object representing the entireset!expression as its argument, and its return value replaces the entireset!expression.Implementation responsibilities: The implementation must check the restrictions on proc only to the extent performed by applying it as described. An implementation may check whether proc is an appropriate argument before applying it.
Transformers can destructure their input with syntax-case and
rebuild their output with syntax.
Each <literal> must be an identifier. Each <syntax-case clause> must take one of the following two forms.
(<pattern> <output expression>) (<pattern> <fender> <output expression>)<fender> and <output expression> must be <expression>s.
A <pattern> is an identifier, constant, or one of the following.
(<pattern> ...) (<pattern> <pattern> ... . <pattern>) (<pattern> ... <pattern> <ellipsis> <pattern> ...) (<pattern> ... <pattern> <ellipsis> <pattern> ... . <pattern>) #(<pattern> ...) #(<pattern> ... <pattern> <ellipsis> <pattern> ...)An <ellipsis> is the identifier ‘...’ (three periods).
An identifier appearing within a <pattern> may be an underscore ‘_’, an ellipsis ‘...’ or a literal identifier listed in the list of literals ‘(<literal> ...)’. All other identifiers appearing within a <pattern> are pattern variables. It is a syntax violation if an ellipsis or underscore appears in ‘(<literal> ...)’.
‘_’ and ‘...’ are the same as in the
(rnrs base (6))library.Pattern variables match arbitrary input subforms and are used to refer to elements of the input. It is a syntax violation if the same pattern variable appears more than once in a <pattern>.
Underscores also match arbitrary input subforms but are not pattern variables and so cannot be used to refer to those elements. Multiple underscores may appear in a <pattern>.
A literal identifier matches an input subform if and only if the input subform is an identifier and either both its occurrence in the input expression and its occurrence in the list of literals have the same lexical binding, or the two identifiers have the same name and both have no lexical binding.
A subpattern followed by an ellipsis can match zero or more elements of the input.
More formally, an input form F matches a pattern P if and only if one of the following holds:
- P is an underscore.
- P is a pattern variable.
- P is a literal identifier and F is an equivalent identifier in the sense of
free-identifier=?.- P is of the form:
(P_1 ... P_n)and F is a list of n elements that match P_1 through P_n.
- P is of the form:
(P_1 ... P_n . P_x)and F is a list or improper list of n or more elements whose first n elements match P_1 through P_n and whose nth cdr matches P_x.
- P is of the form:
(P_1 ... P_k P_e <ellipsis> P_(m+1) ... P_n)where <ellipsis> is the identifier ‘...’ and F is a proper list of n elements whose first k elements match P_1 through P_k, whose next m-k elements each match P_e, and whose remaining n-m elements match P_(m+1) through P_n.
- P is of the form:
(P_1 ... P_k P_e <ellipsis> P_(m+1) ... P_n . P_x)where <ellipsis> is the identifier ‘...’ and F is a list or improper list of n elements whose first k elements match P_1 through P_k, whose next m-k elements each match P_e, whose next n-m elements match P_(m+1) through P_n, and whose nth and final cdr matches P_x.
- P is of the form:
#(P_1 ... P_n)and F is a vector of n elements that match P_1 through P_n.
- P is of the form:
#(P_1 ... P_k P_e <ellipsis> P_(m+1) ... P_n)where <ellipsis> is the identifier ‘...’ and F is a vector of n or more elements whose first k elements match P_1 through P_k, whose next m-k elements each match P_e, and whose remaining n-m elements match P_(m+1) through P_n.
- P is a pattern datum (any nonlist, nonvector, nonsymbol datum) and F is equal to P in the sense of the
equal?procedure.A
syntax-caseexpression first evaluates <expression>. It then attempts to match the <pattern> from the first <syntax-case clause> against the resulting value, which is unwrapped as necessary to perform the match. If the pattern matches the value and no <fender> is present, <output expression> is evaluated and its value returned as the value of thesyntax-caseexpression. If the pattern does not match the value,syntax-casetries the second <syntax-case clause>, then the third, and so on. It is a syntax violation if the value does not match any of the patterns.If the optional <fender> is present, it serves as an additional constraint on acceptance of a clause. If the <pattern> of a given <syntax-case clause> matches the input value, the corresponding <fender> is evaluated. If <fender> evaluates to a true value, the clause is accepted; otherwise, the clause is rejected as if the pattern had failed to match the value. Fenders are logically a part of the matching process, i.e., they specify additional matching constraints beyond the basic structure of the input.
Pattern variables contained within a clause's <pattern> are bound to the corresponding pieces of the input value within the clause's <fender> (if present) and <output expression>. Pattern variables can be referenced only within
syntaxexpressions (see below). Pattern variables occupy the same name space as program variables and keywords.If the
syntax-caseform is in tail context, the <output expression>s are also in tail position.
A
syntaxexpression is similar to aquoteexpression except that:
- The values of pattern variables appearing within <template> are inserted into <template>.
- Contextual information associated both with the input and with the template is retained in the output to support lexical scoping.
- The value of a
syntaxexpression is a syntax object.The following sharp–quote expression:
#'<template>is equivalent to:
(syntax <template>)lang lex datum abbreviations for the other abbreviations.
A <template> can be one among: a pattern variable, an identifier that is not a pattern variable, a pattern datum, or one of the following.
(<subtemplate> ...) (<subtemplate> ... . <template>) #(<subtemplate> ...)A <subtemplate> is a <template> followed by zero or more ellipses.
The value of a
syntaxform is a copy of <template> in which the pattern variables appearing within the template are replaced with the input subforms to which they are bound:
- Pattern data and identifiers that are not pattern variables or ellipses are copied directly into the output.
- A subtemplate followed by an ellipsis expands into zero or more occurrences of the subtemplate.
- Pattern variables that occur in subpatterns followed by one or more ellipses may occur only in subtemplates that are followed by (at least) as many ellipses:
- These pattern variables are replaced in the output by the input subforms to which they are bound, distributed as specified.
- If a pattern variable is followed by more ellipses in the subtemplate than in the associated subpattern, the input form is replicated as necessary.
- The subtemplate must contain at least one pattern variable from a subpattern followed by an ellipsis, and for at least one such pattern variable, the subtemplate must be followed by exactly as many ellipses as the subpattern in which the pattern variable appears (otherwise, the expander would not be able to determine how many times the subform should be repeated in the output).
It is a syntax violation if the above constraints are not met.
A template of the form ‘(<ellipsis> <template>)’ is identical to <template>, except that ellipses within the template have no special meaning. That is, any ellipses contained within <template> are treated as ordinary identifiers. In particular, the template ‘(... ...)’ produces a single ellipsis. This allows macro uses to expand into forms containing ellipses.
The output produced by
syntaxis wrapped or unwrapped according to the following rules:
- The copy of ‘(<t1> . <t2>)’ is a pair if <t1> or <t2> contain any pattern variables.
- The copy of ‘(<t> <ellipsis>)’ is a list if <t> contains any pattern variables.
- The copy of ‘#(<t1> ... <tn>)’ is a vector if any of <t1>, ..., <tn> contain any pattern variables.
- The copy of any portion of <t> not containing any pattern variables is a wrapped syntax object.
The input subforms inserted in place of the pattern variables are wrapped if and only if the corresponding input subforms are wrapped.
The following definitions of or illustrate syntax-case and
syntax; the second is equivalent to the first but uses the
sharp–quote prefix instead of the full syntax form:
(define-syntax or
(lambda (x)
(syntax-case x ()
((_)
(syntax #f))
((_ e)
(syntax e))
((_ e1 e2 e3 ...)
(syntax (let ((t e1))
(if t t (or e2 e3 ...))))))))
(define-syntax or
(lambda (x)
(syntax-case x ()
((_)
#'#f)
((_ e)
#'e)
((_ e1 e2 e3 ...)
#'(let ((t e1))
(if t t (or e2 e3 ...)))))))
The examples below define identifier macros, macro uses supporting
keyword references that do not necessarily appear in the first position
of a list–structured form; the second example uses
make-variable-transformer to handle the case where the keyword
appears on the left-hand side of a set! expression:
(define p (cons 4 5))
(define-syntax p.car
(lambda (x)
(syntax-case x ()
((_ . rest)
#'((car p) . rest))
(_
#'(car p)))))
p.car ⇒ 4
(set! p.car 15) error--> exception &syntax
(define p (cons 4 5))
(define-syntax p.car
(make-variable-transformer
(lambda (x)
(syntax-case x (set!)
((set! _ e)
#'(set-car! p e))
((_ . rest)
#'((car p) . rest))
(_
#'(car p))))))
(set! p.car 15)
p.car ⇒ 15
p ⇒ (15 . 5)
Any syntax-rules form can be expressed with syntax-case by
making the lambda expression and syntax expressions
explicit, and syntax-rules may be defined in terms of
syntax-case as follows.
(define-syntax syntax-rules
(lambda (x)
(syntax-case x ()
((_ (lit ...) ((k . p) t) ...)
(for-all identifier? #'(lit ... k ...))
#'(lambda (x)
(syntax-case x (lit ...)
((_ . p) #'t) ...))))))
The identifier-syntax form of the base library (see baselib transformers) may be defined in terms of syntax-case,
syntax, and make-variable-transformer as follows.
(define-syntax identifier-syntax
(syntax-rules (set!)
((_ e)
(lambda (x)
(syntax-case x ()
(id (identifier? #'id) #'e)
((_ x (... ...)) #'(e x (... ...))))))
((_ (id exp1) ((set! var val) exp2))
(and (identifier? #'id) (identifier? #'var))
(make-variable-transformer
(lambda (x)
(syntax-case x (set!)
((set! var val) #'exp2)
((id x (... ...)) #'(exp1 x (... ...)))
(id (identifier? #'id) #'exp1)))))))
Identifiers are a basic concept of hygienic macro expansion: It is possible to write macros with confidence only with a through understanding of identifiers. Let's analyse a simple form, which we can imagine (for now) to appear at the top level of a program body:
(let ((alpha 123))
alpha)
in it appear two distinct Scheme symbols: ‘let’ once, ‘alpha’ twice; none of them appears in a quoted form, so all of them are identifiers. We have to acknowledge that the number of distinct identifiers is three; the two instances of ‘alpha’ are different identifiers.
Moreover, the first instance of ‘alpha’ appears at the left side of
a <bindings> element of the let syntax, so it is said to be
in “binding position”; the second instance does not appear in a
binding position, so it is said to be in “reference position”.
Binding constructs
Note In the following descriptions, the procedure arguments with name id are meant to be identifiers.
Return
#tif obj is an identifier, i.e., a syntax object representing an identifier, and#fotherwise.The
identifier?procedure is often used within a fender to verify that certain subforms of an input form are identifiers, as in the definition ofrec, which creates self–contained recursive objects, below.(define-syntax rec (lambda (x) (syntax-case x () ((_ x e) (identifier? #'x) #'(letrec ((x e)) x))))) (map (rec fact (lambda (n) (if (= n 0) 1 (* n (fact (- n 1)))))) '(1 2 3 4 5)) ⇒ (1 2 6 24 120) (rec 5 (lambda (x) x)) error--> exception &syntax
The procedures bound-identifier=? and free-identifier=?
each take two identifier arguments and return #t if their arguments
are equivalent and #f otherwise. These predicates are used to
compare identifiers according to their intended use as free
references or bound identifiers in a given context.
Return
#tif a binding for one would capture a reference to the other in the output of the transformer, assuming that the reference appears within the scope of the binding, and#fotherwise.In general, two identifiers are
bound-identifier=?only if both are present in the original program or both are introduced by the same transformer application (perhaps implicitly, seedatum->syntax).Operationally, two identifiers are considered equivalent by
bound-identifier=?if and only if they have the same name and same marks.The
bound-identifier=?procedure can be used for detecting duplicate identifiers in a binding construct or for other preprocessing of a binding construct that requires detecting instances of the bound identifiers.For example, let's consider this macro:
(define-syntax doit (lambda (stx) (syntax-case stx () ((_ id1 id2) (begin (display (bound-identifier=? #'id1 #'id2)) #'(let ((id1 123)) id2))))))in the following macro use the identifiers are
bound-identifier=?:(doit alpha alpha) ⇒ 123 -| #tbecause binding one in the output form of the macro use, “captures” the reference of the other one; in the following macro use the identifiers are not
bound-identifier=?:(let ((beta 456)) (doit alpha beta)) ⇒ 456 -| #fbecause binding one does not capture the reference of the other.
Return
#tif and only if the two identifiers would resolve to the same binding if both were to appear in the output of a transformer outside of any bindings inserted by the transformer. (If neither of two like–named identifiers resolves to a binding, i.e., both are unbound, they are considered to resolve to the same binding.)Operationally, two identifiers are considered equivalent by
free-identifier=?if and only if the topmost matching substitution for each maps to the same binding or the identifiers have the same name and no matching substitution.For example, let's consider this macro:
(define-syntax doit (lambda (stx) (syntax-case stx () ((_ id1 id2) (begin (display (free-identifier=? #'id1 #'id2)) #'(list id1 id2))))))in the following macro use the identifiers are
free-identifier=?:(let ((alpha 123)) (doit alpha alpha)) ⇒ (123 123) -| #tbecause if they are left free in the output form of the macro use, both are captured by the same binding; in the following macro use the identifiers are not
free-identifier=?:(let ((alpha 123) (beta 456)) (doit alpha beta)) ⇒ (123 456) -| #fbecause if they are left free in the output form of the macro use, they are captured by different bindings; in the following macro uses, the identifiers are still not
free-identifier=?:(let ((alpha 123)) (define beta alpha) (doit alpha beta)) (let ((alpha 123)) (let-syntax ((beta (identifier-syntax alpha))) (doit alpha beta)))
The syntax-case and syntax-rules forms internally use
free-identifier=? to compare identifiers listed in the literals
list against input identifiers:
(let ((fred 17))
(define-syntax a
(lambda (x)
(syntax-case x ()
((_ id) #'(b id fred)))))
(define-syntax b
(lambda (x)
(syntax-case x ()
((_ id1 id2)
#`(list
#,(free-identifier=? #'id1 #'id2)
#,(bound-identifier=? #'id1 #'id2))))))
(a fred))
⇒ (#t #f)
The following definition of unnamed let uses
bound-identifier=? to detect duplicate identifiers:
(define-syntax let
(lambda (x)
(define (unique-ids? ls)
(or (null? ls)
(and (let notmem? ((x (car ls))
(ls (cdr ls)))
(or (null? ls)
(and (not (bound-identifier=? x (car ls)))
(notmem? x (cdr ls)))))
(unique-ids? (cdr ls)))))
(syntax-case x ()
((_ ((i v) ...) e1 e2 ...)
(unique-ids? #'(i ...))
#'((lambda (i ...) e1 e2 ...) v ...)))))
the argument ‘#'(i ...)’ to unique-ids? is guaranteed to be
a list by the rules given in the description of syntax.
With this definition of let:
(let ((a 3) (a 4))
(+ a a))
error--> exception &syntax
however:
(let-syntax
((dolet (lambda (x)
(syntax-case x ()
((_ b)
#'(let ((a 3) (b 4)) (+ a b)))))))
(dolet a))
⇒ 7
since the identifier ‘a’ introduced by dolet and the
identifier a extracted from the input form are not
bound-identifier=?.
Rather than including ‘else’ in the literals list as before, this
version of case explicitly tests for ‘else’ using
free-identifier=?.
(define-syntax case
(lambda (x)
(syntax-case x ()
((_ e0 ((k ...) e1 e2 ...) ...
(else-key else-e1 else-e2 ...))
(and (identifier? #'else-key)
(free-identifier=? #'else-key #'else))
#'(let ((t e0))
(cond
((memv t '(k ...)) e1 e2 ...)
...
(else else-e1 else-e2 ...))))
((_ e0 ((ka ...) e1a e2a ...)
((kb ...) e1b e2b ...) ...)
#'(let ((t e0))
(cond
((memv t '(ka ...)) e1a e2a ...)
((memv t '(kb ...)) e1b e2b ...)
...))))))
With either definition of case, ‘else’ is not recognized as
an auxiliary keyword if an enclosing lexical binding for ‘else’
exists. For example,
(let ((else #f))
(case 0 (else (write "oops"))))
error--> exception &syntax
since ‘else’ is bound lexically and is therefore not the same
‘else’ that appears in the definition of case.
Strip all syntactic information from a syntax object and returns the corresponding Scheme datum.
Identifiers stripped in this manner are converted to their symbolic names, which can then be compared with
eq?. Thus, a predicatesymbolic-identifier=?might be defined as follows.(define (symbolic-identifier=? x y) (eq? (syntax->datum x) (syntax->datum y)))
template-id must be a template identifier and datum should be a datum value.
The
datum->syntaxprocedure returns a syntax-object representation of datum that contains the same contextual information as template-id, with the effect that the syntax object behaves as if it were introduced into the code when template-id was introduced.The
datum->syntaxprocedure allows a transformer to “bend” lexical scoping rules by creating implicit identifiers that behave as if they were present in the input form, thus permitting the definition of macros that introduce visible bindings for or references to identifiers that do not appear explicitly in the input form. For example, the following defines aloopexpression that uses this controlled form of identifier capture to bind the variable ‘break’ to an escape procedure within the loop body. (The derivedwith-syntaxform is likeletbut binds pattern variables.)(define-syntax loop (lambda (x) (syntax-case x () ((k e ...) (with-syntax ((break (datum->syntax #'k 'break))) #'(call-with-current-continuation (lambda (break) (let f () e ... (f))))))))) (let ((n 3) (ls '())) (loop (when (= n 0) (break ls)) (set! ls (cons 'a ls)) (set! n (- n 1)))) ⇒ (a a a)Were
loopto be defined as:(define-syntax loop (lambda (x) (syntax-case x () ((_ e ...) #'(call-with-current-continuation (lambda (break) (let f () e ... (f))))))))the variable ‘break’ would not be visible in ‘e ...’.
The datum argument datum may also represent an arbitrary Scheme form, as demonstrated by the following definition of
include.(define-syntax include (lambda (x) (define (read-file fn k) (let ((p (open-file-input-port fn))) (let loop ((x (get-datum p))) (if (eof-object? x) (begin (close-port p) '()) (cons (datum->syntax k x) (loop (get-datum p))))))) (syntax-case x () ((k filename) (let ((fn (syntax->datum #'filename))) (with-syntax (((exp ...) (read-file fn #'k))) #'(begin exp ...)))))))
(include "filename")expands into abeginexpression containing the forms found in the file named by ‘"filename"’. For example, if the file flib.ss contains:(define f (lambda (x) (g (* x x))))and the file glib.ss contains:
(define g (lambda (x) (+ x x)))the expression:
(let () (include "flib.ss") (include "glib.ss") (f 5))evaluates to 50.
The definition of
includeusesdatum->syntaxto convert the objects read from the file into syntax objects in the proper lexical context, so that identifier references and definitions within those expressions are scoped where theincludeform appears.
Transformers can introduce a fixed number of identifiers into their
output simply by naming each identifier. In some cases, however, the
number of identifiers to be introduced depends upon some characteristic
of the input expression. A straightforward definition of letrec,
for example, requires as many temporary identifiers as there are binding
pairs in the input expression. The procedure
generate-temporaries is used to construct lists of temporary
identifiers.
l must be be a list or syntax object representing a list–structured form; its contents are not important.
The number of temporaries generated is the number of elements in l. Each temporary is guaranteed to be unique, i.e., different from all other identifiers.
A definition of
letrecequivalent to the one usingsyntax-rulesgiven in lang derived is shown below.(define-syntax letrec (lambda (x) (syntax-case x () ((_ ((i e) ...) b1 b2 ...) (with-syntax (((T ...) (generate-temporaries #'(i ...)))) #'(let ((i #f) ...) (let ((T e) ...) (set! i T) ... (let () b1 b2 ...))))))))This version uses
generate-temporariesinstead of recursively defined helper to generate the necessary temporaries.
The forms and procedures described in this section can be defined in terms of the forms and procedures described in earlier sections of this chapter.
The
with-syntaxform is used to bind pattern variables, just asletis used to bind variables. This allows a transformer to construct its output in separate pieces, then put the pieces together.Each <pattern> is identical in form to a
syntax-casepattern. The value of each <expression> is computed and destructured according to the corresponding <pattern>, and pattern variables within the <pattern> are bound as withsyntax-caseto the corresponding portions of the value within <body>.The
with-syntaxform may be defined in terms ofsyntax-caseas follows.(define-syntax with-syntax (lambda (x) (syntax-case x () ((_ ((p e0) ...) e1 e2 ...) (syntax (syntax-case (list e0 ...) () ((p ...) (let () e1 e2 ...))))))))The following definition of
conddemonstrates the use ofwith-syntaxto support transformers that employ recursion internally to construct their output. It handles allcondclause variations and takes care to produce one-armedifexpressions where appropriate.(define-syntax cond (lambda (x) (syntax-case x () ((_ c1 c2 ...) (let f ((c1 #'c1) (c2* #'(c2 ...))) (syntax-case c2* () (() (syntax-case c1 (else =>) ((else e1 e2 ...) #'(begin e1 e2 ...)) ((e0) #'e0) ((e0 => e1) #'(let ((t e0)) (if t (e1 t)))) ((e0 e1 e2 ...) #'(if e0 (begin e1 e2 ...))))) ((c2 c3 ...) (with-syntax ((rest (f #'c2 #'(c3 ...)))) (syntax-case c1 (=>) ((e0) #'(let ((t e0)) (if t t rest))) ((e0 => e1) #'(let ((t e0)) (if t (e1 t) rest))) ((e0 e1 e2 ...) #'(if e0 (begin e1 e2 ...) rest)))))))))))
The
quasisyntaxform is similar tosyntax, but it allows parts of the quoted text to be evaluated, in a manner similar to the operation ofquasiquote(see baselib quasiquotation).Within a
quasisyntaxtemplate, subforms ofunsyntaxandunsyntax-splicingforms are evaluated, and everything else is treated as ordinary template material, as withsyntax.The value of each
unsyntaxsubform is inserted into the output in place of theunsyntaxform, while the value of eachunsyntax-splicingsubform is spliced into the surrounding list or vector structure. Uses ofunsyntaxandunsyntax-splicingare valid only withinquasisyntaxexpressions.A
quasisyntaxexpression may be nested, with eachquasisyntaxintroducing a new level of syntax quotation and eachunsyntaxorunsyntax-splicingtaking away a level of quotation. An expression nested within nquasisyntaxexpressions must be within nunsyntaxorunsyntax-splicingexpressions to be evaluated.As noted in lang lex datum abbreviations:
#`<template> == (quasisyntax <template>) #,<template> == (unsyntax <template>) #,@<template> == (unsyntax-splicing <template>)The
quasisyntaxkeyword can be used in place ofwith-syntaxin many cases. For example, the definition ofcaseshown under the description ofwith-syntaxabove can be rewritten usingquasisyntaxas follows.(define-syntax case (lambda (x) (syntax-case x () ((_ e c1 c2 ...) #`(let ((t e)) #,(let f ((c1 #'c1) (cmore #'(c2 ...))) (if (null? cmore) (syntax-case c1 (else) ((else e1 e2 ...) #'(begin e1 e2 ...)) (((k ...) e1 e2 ...) #'(when (memv t '(k ...)) (begin e1 e2 ...)))) (syntax-case c1 () (((k ...) e1 e2 ...) #`(if (memv t '(k ...)) (begin e1 e2 ...) #,(f (car cmore) (cdr cmore))))))))))))Uses of
unsyntaxandunsyntax-splicingwith zero or more than one subform are valid only in splicing (list or vector) contexts.(unsyntaxtemplate...)is equivalent to(unsyntaxtemplate) ..., and(unsyntax-splicingtemplate...)is equivalent to(unsyntax-splicingtemplate) .... These forms are primarily useful as intermediate forms in the output of thequasisyntaxexpander.NOTE Uses ofunsyntaxandunsyntax-splicingwith zero or more than one subform enable certain idioms, such as ‘#,@#,@’, which has the effect of a doubly indirect splicing when used within a doubly nested and doubly evaluatedquasisyntaxexpression, as with the nestedquasiquoteexamples shown in section baselib quasiquotation.
who must be
#for a string or a symbol. message must be a string. form must be a syntax object or a datum value. subform must be a syntax object or a datum value.The
syntax-violationprocedure raises an exception, reporting a syntax violation. who should describe the macro transformer that detected the exception. The message argument should describe the violation. form should be the erroneous source syntax object or a datum value representing a form. The optional subform argument should be a syntax object or datum value representing a form that more precisely locates the violation.If who is
#f,syntax-violationattempts to infer an appropriate value for the condition object (see below) as follows: When form is either an identifier or a list-structured syntax object containing an identifier as its first element, then the inferred value is the identifier's symbol. Otherwise, no value for who is provided as part of the condition object.The condition object provided with the exception has the following condition types:
- If who is not
#for can be inferred, the condition has condition type ‘&who’, with who as the value of its field. In that case, who should identify the procedure or entity that detected the exception. If it is#f, the condition does not have condition type ‘&who’.- The condition has condition type ‘&message’, with message as the value of its field.
- The condition has condition type
&syntaxwith form and subform as the value of its fields. If subform is not provided, the value of the subform field is#f.
Using datum->syntax, it is even possible to break hygiene
entirely and write macros in the style of old Lisp macros. The
lisp-transformer procedure defined below creates a transformer
that converts its input into a datum, calls the programmer's procedure
on this datum, and converts the result back into a syntax object scoped
where the original macro use appeared:
(define (lisp-transformer proc)
(lambda (stx)
(syntax-case stx ()
((use . rest)
(datum->syntax #'use (proc (syntax->datum stx)))))))
This transformer can be used as follows, first create a library with the definition in it:
(library (lisp-trans)
(export lisp-transformer)
(import (rnrs))
(define (lisp-transformer proc)
(lambda (stx)
(syntax-case stx ()
((use . rest)
(datum->syntax #'use (proc (syntax->datum stx))))))))
then import the library for the ‘expand’ phase; the following example shows what is handed to the client procedure:
(import (rnrs)
(for (lisp-trans) expand))
(define-syntax print-it
(lisp-transformer (lambda (thing)
(write thing)
(newline)
#f)))
(print-it (1 2 3))
-| (print-it (1 2 3))
notice that the macro use prints the value of thing, then returns
#f which is evaulated in place of the macro use and the result is
#f itself.
Now we shold understand the following:
(import (rnrs)
(for (lisp-trans) expand))
(define-syntax even
(lisp-transformer
(lambda (thing)
`(begin
(write (quote ,(map even? (cadr thing))))
(newline)))))
(even (1 2 3))
-| (#f #t #f)
the macro use (even (1 2 3)) is equivalent to:
(begin
(write (quote (#f #t #f)))
(newline))
that is, the form returned by the client procedure is evaluated in place of the macro use.
The (rnrs hashtables (6)) library provides a set of operations on
hashtables.
A hashtable is a data structure that associates keys with values. Any object can be used as a key, provided a hash function and a suitable equivalence function is available.
A hash function is a procedure that maps keys to exact integer objects.
It is the programmer's responsibility to ensure that the hash function
is compatible with the equivalence function, which is a procedure that
accepts two keys and returns true if they are equivalent and #f
otherwise.
Standard hashtables for arbitrary objects based on the eq? and
eqv? predicates (baselib predicates) are provided. Also,
hash functions for arbitrary objects, strings, and symbols are provided.
This section uses the hashtable parameter name for arguments that must be hashtables, and the key parameter name for arguments that must be hashtable keys.
Return a newly allocated mutable hashtable that accepts arbitrary objects as keys, and compares those keys with
eq?. If an argument is given, the initial capacity of the hashtable is set to approximately k elements.
Return a newly allocated mutable hashtable that accepts arbitrary objects as keys, and compares those keys with
eqv?. If an argument is given, the initial capacity of the hashtable is set to approximately k elements.
hash-function and equiv must be procedures. hash-function should accept a key as an argument and should return a non–negative exact integer object. equiv should accept two keys as arguments and return a single value. Neither procedure should mutate the hashtable returned by
make-hashtable.The
make-hashtableprocedure returns a newly allocated mutable hashtable using hash-function as the hash function and equiv as the equivalence function used to compare keys. If a third argument is given, the initial capacity of the hashtable is set to approximately k elements.Both hash-function and equiv should behave like pure functions on the domain of keys. For example, the
string-hashandstring=?procedures are permissible only if all keys are strings and the contents of those strings are never changed so long as any of them continues to serve as a key in the hashtable. Furthermore, any pair of keys for which equiv returns true should be hashed to the same exact integer objects by hash-function.Implementation responsibilities: The implementation must check the restrictions on hash-function and equiv to the extent performed by applying them as described.
NOTE Hashtables are allowed to cache the results of calling the hash function and equivalence function, so programs cannot rely on the hash function being called for every lookup or update. Furthermore any hashtable operation may call the hash function more than once.
Return the number of keys contained in hashtable as an exact integer object.
Return the value in hashtable associated with key. If hashtable does not contain an association for key, default is returned.
Change hashtable to associate key with obj, adding a new association or replacing any existing association for key, and returns unspecified values.
Remove any association for key within hashtable and returns unspecified values.
Return
#tif hashtable contains an association for key,#fotherwise.
proc should accept one argument, should return a single value, and should not mutate hashtable.
The
hashtable-update!procedure applies proc to the value in hashtable associated with key, or to default if hashtable does not contain an association for key. The hashtable is then changed to associate key with the value returned by proc.The behavior of
hashtable-update!is equivalent to the following code, but may be implemented more efficiently in cases where the implementation can avoid multiple lookups of the same key:(hashtable-set! hashtable key (proc (hashtable-ref hashtable key default)))
Return a copy of hashtable. If the mutable argument is provided and is true, the returned hashtable is mutable; otherwise it is immutable.
Remove all associations from hashtable and returns unspecified values.
If a second argument is given, the current capacity of the hashtable is reset to approximately k elements.
Return a vector of all keys in hashtable. The order of the vector is unspecified.
Return two values, a vector of the keys in hashtable, and a vector of the corresponding values.
Example:
(let ((h (make-eqv-hashtable))) (hashtable-set! h 1 'one) (hashtable-set! h 2 'two) (hashtable-set! h 3 'three) (hashtable-entries h)) ⇒ #(1 2 3) #(one two three) ; two return valuesthe order of the entries in the result vectors is not known.
Return the equivalence function used by hashtable to compare keys. For hashtables created with
make-eq-hashtableandmake-eqv-hashtable, returnseq?andeqv?respectively.
Return the hash function used by hashtable. For hashtables created by
make-eq-hashtableormake-eqv-hashtable,#fis returned.
The equal-hash, string-hash, and string-ci-hash
procedures of this section are acceptable as the hash functions of a
hashtable only if the keys on which they are called are not mutated
while they remain in use as keys in the hashtable.
Return an integer hash value for obj, based on its structure and current contents. This hash function is suitable for use with
equal?as an equivalence function.NOTE Likeequal?, theequal-hashprocedure must always terminate, even if its arguments contain cycles.
Return an integer hash value for string, based on its current contents. This hash function is suitable for use with
string=?as an equivalence function.
Return an integer hash value for string based on its current contents, ignoring case. This hash function is suitable for use with
string-ci=?as an equivalence function.
This chapter describes the (rnrs enums (6)) library for dealing with
enumerated values and sets of enumerated values. Enumerated values are
represented by ordinary symbols, while finite sets of enumerated values
form a separate type, known as the enumeration sets. The
enumeration sets are further partitioned into sets that share the same
universe and enumeration type. These universes and
enumeration types are created by the make-enumeration procedure.
Each call to that procedure creates a new enumeration type.
This library interprets each enumeration set with respect to its specific universe of symbols and enumeration type. This facilitates efficient implementation of enumeration sets and enables the complement operation.
In the descriptions of the following procedures, enum-set ranges
over the enumeration sets, which are defined as the subsets of the
universes that can be defined using make-enumeration.
symbol-list must be a list of symbols.
The
make-enumerationprocedure creates a new enumeration type whose universe consists of those symbols (in canonical order of their first appearance in the list) and returns that universe as an enumeration set whose universe is itself and whose enumeration type is the newly created enumeration type.
Return the set of all symbols that comprise the universe of its argument, as an enumeration set.
Return a unary procedure that, given a symbol that is in the universe of enum-set, returns its 0–origin index within the canonical ordering of the symbols in the universe; given a symbol not in the universe, the unary procedure returns
#f.(let* ((e (make-enumeration '(red green blue))) (i (enum-set-indexer e))) (list (i 'red) (i 'green) (i 'blue) (i 'yellow))) ⇒ (0 1 2 #f)The
enum-set-indexerprocedure could be defined as follows using thememqprocedure from the(rnrs lists (6))library:(define (enum-set-indexer set) (let* ((symbols (enum-set->list (enum-set-universe set))) (cardinality (length symbols))) (lambda (x) (cond ((memq x symbols) => (lambda (probe) (- cardinality (length probe)))) (else #f)))))
Return a unary procedure that, given a list of symbols that belong to the universe of enum-set, returns a subset of that universe that contains exactly the symbols in the list. The values in the list must all belong to the universe.
Return a list of the symbols that belong to its argument, in the canonical order of the universe of enum-set.
(let* ((e (make-enumeration '(red green blue))) (c (enum-set-constructor e))) (enum-set->list (c '(blue red)))) ⇒ (red blue)
The
enum-set-member?procedure returns#tif its first argument is an element of its second argument,#fotherwise.The
enum-set-subset?procedure returns#tif the universe of enum-set1 is a subset of the universe of enum-set2 (considered as sets of symbols) and every element of enum-set1 is a member of enum-set2. It returns#fotherwise.The
enum-set=?procedure returns#tif enum-set1 is a subset of enum-set2 and vice versa, as determined by theenum-set-subset?procedure. This implies that the universes of the two sets are equal as sets of symbols, but does not imply that they are equal as enumeration types. Otherwise,#fis returned.(let* ((e (make-enumeration '(red green blue))) (c (enum-set-constructor e))) (list (enum-set-member? 'blue (c '(red blue))) (enum-set-member? 'green (c '(red blue))) (enum-set-subset? (c '(red blue)) e) (enum-set-subset? (c '(red blue)) (c '(blue red))) (enum-set-subset? (c '(red blue)) (c '(red))) (enum-set=? (c '(red blue)) (c '(blue red))))) ⇒ (#t #f #t #t #f #t)
enum-set1 and enum-set2 must be enumeration sets that have the same enumeration type.
The
enum-set-unionprocedure returns the union of enum-set1 and enum-set2. Theenum-set-intersectionprocedure returns the intersection of enum-set1 and enum-set2. Theenum-set-differenceprocedure returns the difference of enum-set1 and enum-set2.(let* ((e (make-enumeration '(red green blue))) (c (enum-set-constructor e))) (list (enum-set->list (enum-set-union (c '(blue)) (c '(red)))) (enum-set->list (enum-set-intersection (c '(red green)) (c '(red blue)))) (enum-set->list (enum-set-difference (c '(red green)) (c '(red blue)))))) ⇒ ((red blue) (red) (green))
Return enum-set's complement with respect to its universe.
(let* ((e (make-enumeration '(red green blue))) (c (enum-set-constructor e))) (enum-set->list (enum-set-complement (c '(red))))) ⇒ (green blue)
Project enum-set1 into the universe of enum-set2, dropping any elements of enum-set1 that do not belong to the universe of enum-set2. (If enum-set1 is a subset of the universe of its second, no elements are dropped, and the injection is returned.)
(let ((e1 (make-enumeration '(red green blue black))) (e2 (make-enumeration '(red black white)))) (enum-set->list (enum-set-projection e1 e2)))) ⇒ (red black)
The
define-enumerationform defines an enumeration type and provides two macros for constructing its members and sets of its members.A
define-enumerationform is a definition and can appear anywhere any other <definition> can appear.<type-name> is an identifier that is bound as a syntactic keyword; <symbol> ... are the symbols that comprise the universe of the enumeration (in order).
(<type-name> <symbol>)checks at macro-expansion time whether the name of <symbol> is in the universe associated with <type-name>. If it is,(<type-name> <symbol>)is equivalent to<symbol>. It is a syntax violation if it is not.<constructor-syntax> is an identifier that is bound to a macro that, given any finite sequence of the symbols in the universe, possibly with duplicates, expands into an expression that evaluates to the enumeration set of those symbols.
(<constructor-syntax> <symbol> ...)checks at macro-expansion time whether every <symbol> ... is in the universe associated with <type-name>. It is a syntax violation if one or more is not. Otherwise:(<constructor-syntax> <symbol> ...)is equivalent to:
((enum-set-constructor (<constructor-syntax>)) '(<symbol> ...))Example:
(define-enumeration color (black white purple maroon) color-set) (color black) ⇒ black (color purpel) ⇒ exception &syntax (enum-set->list (color-set)) ⇒ () (enum-set->list (color-set maroon white)) ⇒ (white maroon)NOTE In(<type-name> <symbol>)and(<constructor-syntax> <symbol> ...)forms, only the names of the <symbol>s are significant.
The (rnrs (6)) library is a composite of most of the libraries
described in this report. The only exceptions are:
(rnrs eval (6))(rnrs mutable-pairs (6))(rnrs mutable-strings (6))(rnrs r5rs (6))The library exports all procedures and syntactic forms provided by the component libraries.
All of the bindings exported by (rnrs (6)) are exported for both
run and expand; lang library import export.
The (rnrs eval (6)) library allows a program to create Scheme
expressions as data at run time and evaluate them.
Evaluate expression in the specified environment and returns its value. expression must be a syntactically valid Scheme expression represented as a datum value, and environment must be an environment, which can be created using the
environmentprocedure described below.If the first argument to
evalis determined not to be a syntactically correct expression, thenevalmust raise an exception with condition type&syntax. Specifically, if the first argument toevalis a definition or a splicingbeginform containing a definition, it must raise an exception with condition type&syntax.
import-spec must be a datum representing an <import spec> (lang library form).
The
environmentprocedure returns an environment corresponding to import-spec.The bindings of the environment represented by the specifier are immutable: If
evalis applied to an expression that is determined to contain an assignment to one of the variables of the environment, thenevalmust raise an exception with a condition type&syntax.(library (foo) (export) (import (rnrs) (rnrs eval)) (write (eval '(let ((x 3)) x) (environment '(rnrs))))) ;; writes 3 (library (foo) (export) (import (rnrs) (rnrs eval)) (write (eval '(eval:car (eval:cons 2 4)) (environment '(prefix (only (rnrs) car cdr cons null?) eval:))))) ;; writes 2
The procedures provided by the (rnrs mutable-pairs (6)) library allow
new values to be assigned to the car and cdr fields of previously
allocated pairs.
Store obj in the car field of pair. The
set-car!procedure returns unspecified values.(define (f) (list 'not-a-constant-list)) (define (g) '(constant-list)) (set-car! (f) 3) ⇒ unspecified (set-car! (g) 3) ⇒ unspecified ; should raise exception &assertionIf an immutable pair is passed to
set-car!, an exception with condition type&assertionshould be raised.
Store obj in the cdr field of pair. The
set-cdr!procedure returns unspecified values.If an immutable pair is passed to
set-cdr!, an exception with condition type&assertionshould be raised.(let ((x (list 'a 'b 'c 'a)) (y (list 'a 'b 'c 'a 'b 'c 'a))) (set-cdr! (list-tail x 2) x) (set-cdr! (list-tail y 5) y) (list (equal? x x) (equal? x y) (equal? (list x y 'a) (list y x 'b)))) ⇒ (#t #t #f)
The string-set! procedure provided by the
(rnrs mutable-strings (6)) library allows mutating the characters of
a string in–place.
k must be a valid index of string.
The
string-set!procedure stores char in element k of string and returns unspecified values.Passing an immutable string to
string-set!should cause an exception with condition type&assertionto be raised.(define (f) (make-string 3 #\*)) (define (g) "***") (string-set! (f) 0 #\?) ⇒ unspecified (string-set! (g) 0 #\?) ⇒ unspecified ; should raise exception &assertion (string-set! (symbol->string 'immutable) 0 #\?) ⇒ unspecified ; should raise exception &assertionNOTE Implementors should makestring-set!run in constant time.
Store char in every element of the given string and returns unspecified values.
The features described in this chapter are exported from the
(rnrs r5rs (6)) library and provide some functionality of the
preceding revision of this report that was omitted from the main part of
the current report.
These are the same as the
inexactandexactprocedures; baselib math ops exactness.
These procedures implement number–theoretic (integer) division. N2 must be non–zero. All three procedures return integer objects. If n1/n2 is an integer object:
(quotient n1 n2) ⇒ n1/n2 (remainder n1 n2) ⇒ 0 (modulo n1 n2) ⇒ 0If n1/n2 is not an integer object:
(quotient n1 n2) ⇒ n_q (remainder n1 n2) ⇒ n_r (modulo n1 n2) ⇒ n_mwhere n_q is n1/n2 rounded towards zero,
0 < |n_r| < |n2| 0 < |n_m| < |n2|n_r and n_m differ from n1 by a multiple of n2, n_r has the same sign as n1, and n_m has the same sign as n2.
Consequently, for integer objects n1 and n2 with n2 not equal to 0,
(= n1 (+ (* n2 (quotient n1 n2)) (remainder n1 n2))) ⇒ #tprovided all number object involved in that computation are exact.
(modulo 13 4) ⇒ 1 (remainder 13 4) ⇒ 1 (modulo -13 4) ⇒ 3 (remainder -13 4) ⇒ -1 (modulo 13 -4) ⇒ -3 (remainder 13 -4) ⇒ 1 (modulo -13 -4) ⇒ -1 (remainder -13 -4) ⇒ -1 (remainder -13 -4.0) ⇒ -1.0NOTE These procedures could be defined in terms ofdivandmod(baselib math ops arithmetic) as follows (without checking of the argument types):(define (sign n) (cond ((negative? n) -1) ((positive? n) 1) (else 0))) (define (quotient n1 n2) (* (sign n1) (sign n2) (div (abs n1) (abs n2)))) (define (remainder n1 n2) (* (sign n1) (mod (abs n1) (abs n2)))) (define (modulo n1 n2) (* (sign n2) (mod (* (sign n2) n1) (abs n2))))
The
delayconstruct is used together with the procedureforceto implement lazy evaluation or call by need.
(delay <expression>)returns an object called a promise which at some point in the future may be asked (by theforceprocedure) to evaluate <expression>, and deliver the resulting value. The effect of <expression> returning multiple values is unspecified.
promise must be a promise.
The
forceprocedure forces the value of promise. If no value has been computed for the promise, then a value is computed and returned. The value of the promise is cached (or “memoized”) so that if it is forced a second time, the previously computed value is returned.(force (delay (+ 1 2))) ⇒ 3 (let ((p (delay (+ 1 2)))) (list (force p) (force p))) ⇒ (3 3) (define a-stream (letrec ((next (lambda (n) (cons n (delay (next (+ n 1))))))) (next 0))) (define head car) (define tail (lambda (stream) (force (cdr stream)))) (head (tail (tail a-stream))) ⇒ 2Promises are mainly intended for programs written in functional style. The following examples should not be considered to illustrate good programming style, but they illustrate the property that only one value is computed for a promise, no matter how many times it is forced.
(define count 0) (define p (delay (begin (set! count (+ count 1)) (if (> count x) count (force p))))) (define x 5) p ⇒ a promise (force p) ⇒ 6 p ⇒ a promise, still (begin (set! x 10) (force p)) ⇒ 6Here is a possible implementation of
delayandforce. Promises are implemented here as procedures of no arguments, andforcesimply calls its argument:(define force (lambda (object) (object)))The expression:
(delay <expression>)has the same meaning as the procedure call:
(make-promise (lambda () <expression>))as follows:
(define-syntax delay (syntax-rules () ((delay expression) (make-promise (lambda () expression)))))where
make-promiseis defined as follows:(define make-promise (lambda (proc) (let ((result-ready? #f) (result #f)) (lambda () (if result-ready? result (let ((x (proc))) (if result-ready? result (begin (set! result-ready? #t) (set! result x) result))))))))
n must be the exact integer object 5.
The
null-environmentprocedure returns an environment specifier suitable for use withevalrepresenting an environment that is empty except for the (syntactic) bindings for all keywords described in the previous revision of this report, including bindings for ‘=>’, ‘...’, ‘else’ and ‘_’ that are the same as those in the(rnrs base (6))library.
n must be the exact integer object 5.
The
scheme-report-environmentprocedure returns an environment specifier for an environment that is empty except for the bindings for the identifiers described in the previous revision of this report, omittingload,interaction-environment,transcript-on,transcript-off, andchar-ready?.The variable bindings have as values the procedures of the same names described in this report, and the keyword bindings, including ‘=>’, ‘...’, ‘else’ and ‘_’ are the same as those described in this report.
(nausicaa) languageThis chapter describes the (nausicaa) language and its
differences from the (rnrs) language. Most of them are the
revised implementations of SRFI specifications that were voted to the
final status.
It is desirable that programs which depend on additions to the standard
Scheme language name those additions. For example the SRFIs provide
the specifications of some additions (“features”). The
cond-expand syntax described below provides the means to actually
check that features are present in the Scheme system.
A particular functionality may exist in several or even most Scheme systems but its API may be different (use of a procedure or special–form, name, number of parameters, etc). To write code that will run on several Scheme systems, it is useful to have a common construct to enable or disable sections of code based on the existence or absence of a feature in the Scheme system being used.
Features are identified by feature identifiers. In order for the semantics of this construct to be well–defined, the feature identifier must of course refer to a feature which has a well–defined meaning. There is thus a need for a registry to keep track of the formal specification associated with each valid feature–identifier.
Test for the existence of features at macro–expansion time. Each clause has the following formal definition:
<clause> -> (<feature requirement> <body>) <feature requirement> -> <feature identifier> | (and <feature requirement>*) | (or <feature requirement>*) | (not <feature requirement>) <feature identifier> -> one of the symbols described below, or "else"
cond-expandeither expands into the <body> of one of its clauses or signals an error during syntactic processing.cond-expandexpands into the body of the first clause whose feature requirement is currently satisfied (theelseclause, if present, is selected if none of the previous clauses is selected).A <feature requirement> has an obvious interpretation as a logical formula, where the <feature identifier> symbols have meaning true if the associated feature is in effect at the location of the
cond-expandform, and#fotherwise. A feature requirement is satisfied if its formula is true under this interpretation.Examples:
(cond-expand ((or linux macos) ---) (windows ---))
What follows is the list of supported features.
ikaruslarcenymoshypsilonlinux-gnuopenbsdwinnt3.5sysv4darwin...AC_CANONICAL_TARGET macro of
GNU Autoconf. See Canonicalizing.
r6rsand-let*and-let* syntax.
begin0begin0 syntax.
char-sets(char-sets) library.
checks(checks) library.
compare(compare) library.
cutcut and cute syntaxes.
loops(loops) library.
format(formations) library.
lists(lists) library.
parametersrandom(randomisations) library.
recursionrecursion syntax.
receivereceive syntax.
sharingstreams(streams) library.
strings(strings) library.
time(time) library.
vectors(vectors) library.
An extension of the R6RS
=function that also accepts zero and one arguments. When called with no arguments, return#t. When called with one argument, return the result of comparing it with itself.
Return
#tif x is strictly positive or strictly negative.
Wrappers for the functions defined by R6RS that apply the predicate to both the real and imaginary parts of z.
The specifications of the R6RS functions mandates that the argument can only be a real number.
Like
maxdefined by R6RS, but if an argument is not–a–number: The result is not–a–number. This changes the behaviour of some Scheme implementations under which:(max 1 +nan.0) ⇒ 1.0
Like
*defined by R6RS, but if an argument is not–a–number: The result is not–a–number. This changes the behaviour of some Scheme implementations under which:(* 0 +nan.0) ⇒ 0because, for those implementations, whatever number object multiplied by exact zero is exact zero.
Convert the symbol or string obj to a string. If obj is a string return it, else apply
symbol->stringto it and return the result.
If obj is a symbol: Convert it to string and return the result; else if it is a string, return it. If it is neither a string nor a symbol: Raise an assertion violation.
The following bindings are exported by the (pretty-print)
library and re–exported by the (nausicaa) library.
Print a human readable representation of obj to port or the return value of
current-output-port.
The following bindings are exported by the (language-extensions)
library and re–exported by the (nausicaa) library.
Like an ordinary
and, anand-let*special form evaluates the expressions in formals in order until the first one that yields#f. Unlikeand, however, a non–#fresult of one expression can be bound to a fresh variable and used in the subsequent expressions.and-let*is a cross–breed betweenlet*andand.formals can be the empty list, or a list of the following elements:
(varname expression)- evaluate expression; if the result is
#f: return#fimmediately, else bind the result to varname; varname is available to the rest of the expressions, and theBODY;- expression
- evaluate expression; if the result is
#f: return#fimmediately;- bound-variable
- look up bound-variable; if the value is
#f; return#fimmediately; bound-varname is available to the rest of the expressions, and the body.If all the expressions and bound-variables in formals evaluate to true, the forms in body are evaluated and the result of the last one returned.
Evaluate all the forms like the standard
begin, but return the value of form0. The implementation comes from the R6RS original document, Appendix A “Formal semantics”.(define-syntax begin0 (syntax-rules () ((_ ?form0 ?form ...) (call-with-values (lambda () ?form0) (lambda x ?form ... (apply values x))))))This syntax is called
prog1in Common Lisp and Emacs Lisp.
Bind the result of expr to var as in
letorlet-values, evaluate the forms in the resulting environment, then return var0 possibly as multiple values.
The mechanism provided for binding identifiers to the values of a multiple–valued expression are
call-with-values,let-values,let*-values.receiveis another concise and readable syntax for creating such bindings.formals can have any of 3 forms:
(variable1 ... variablen)- The environment in which the
receive–expression is evaluated is extended by binding variable1, ..., variablen to fresh locations. The expression is evaluated, and its values are stored into those locations (it is an error if expression does not have exactly n values).variable- The environment in which the
receive–expression is evaluated is extended by binding variable to a fresh location. The expression is evaluated, its values are converted into a newly allocated list, and the list is stored in the location bound to variable.(variable1 ... variablen . variablen+1)- The environment in which the
receive–expression is evaluated is extended by binding variable1, ..., variablen+1 to fresh locations. The expression is evaluated. Its first n values are stored into the locations bound to variable1, ..., variablen. Any remaining values are converted into a newly allocated list, which is stored into the location bound to variablen+1 (it is an error if expression does not have at least n values.In any case, the expressions in body are evaluated sequentially in the extended environment. The results of the last expression in the body are the values of the receive–expression.
When programming in functional style, it is frequently necessary to specialize some of the parameters of a multi–parameter procedure. For example, from the binary operation
consone might want to obtain the unary operation(lambda (x) (cons 1 x)). This specialization of parameters is also known as “partial application”, “operator section” or “projection”.The
cutandcutesyntaxes allow to write this sort of specialization in a simple and compact way. Examples:(cut cons (+ a 1) <>) = (lambda (x2) (cons (+ a 1) x2)) (cut list 1 <> 3 <> 5) = (lambda (x2 x4) (list 1 x2 3 x4 5)) (cut list) = (lambda () (list)) (cut list 1 <> 3 <...>) = (lambda (x2 . xs) (apply list 1 x2 3 xs)) (cut <> a b) = (lambda (f) (f a b))
cutspecializes some of the parameters of its first argument. The parameters that are to show up as formal variables of the result are indicated by the symbol<>, pronouced as “slot”. In addition, the symbol<...>, pronounced as “rest–slot”, matches all residual arguments of a variable argument procedure. The first argument can also be a slot.
cuteevaluates the non–slot expressions at the time the procedure is specialized, not at the time the specialized procedure is called. For example:(cute cons (+ a 1) <>) = (let ((a1 (+ a 1))) (lambda (x2) (cons a1 x2)))
cutewill evaluate(+ a 1)once, while thecutwould evaluate it during every invocation of the resulting procedure.The formal syntax of a specialized expression is:
<cut-expression> -> (cut <slot-or-expr> <slot-or-expr>*) | (cut <slot-or-expr> <slot-or-expr>* <...>) | (cute <slot-or-expr> <slot-or-expr>*) | (cute <slot-or-expr> <slot-or-expr>* <...>) <slot-or-expr> -> <> ; a "slot" | <expression> ; a "non-slot expression"
Allow the simple and non–imperative construction of self–referential expressions. The implementation is:
(define-syntax recursion (syntax-rules () ((_ (?name . ?variables) . ?body) (letrec ((?name (lambda ?variables . ?body))) ?name)) ((_ ?name ?expr) (letrec ((?name ?expr)) ?name))))
recursiongenerates an inner lambda using formals and body; the first identifier in formals is meant to be bound to the inner lambda itself. The return value is a function that invokes the inner lambda with the inner lambda and the given arguments.For example the following:
(define (dummy n) (let loop ((n n)) (if (zero? n) 1 (* n (loop (- n 1)))))) (dummy 5)can be rewritten:
(letrec ((dummy (lambda (loop n) (if (zero? n) 1 (* n (loop (- n 1))))))) (dummy 5)) ⇒ 120which can be written:
((recursion (loop n) (if (zero? n) 1 (* n (loop (- n 1))))) 5) ⇒ 120
Like
do, but bind the variables as inlet*rather than as inlet. The <step> form is optional.
Loop evaluating the forms and assigning varname to an integer from zero inclusive to exclusive-count. Return result, or
#fif result is not given.Example:
(dotimes (i 100) (display i) (newline))prints the integers in the range
[0, 99].
Loop evaluating the forms and assigning varname to the elements from list. Return result, or
#fif result is not given.Example:
(dolist (elm '(1 2 3)) (display elm) (newline))prints 1, 2, 3.
Loop evaluating the forms and assigning varname to the elements from list. Return result, or
#fif result is not given. The loop is broken if test evaluates to true.Example:
(loop-upon-list (i '(1 2 3 4) 'retval) (break-when (= i 3)) (display i) (newline))prints
1and2, then returnsretval.
Evaluate the forms in the
byandelse-byclauses, in sequence, until test evaluates to true. If the first evaluation of test gives true: no forms are evaluated. If neither thebynor theelse-byforms succeed in making test true: the forms in theelseclause are evaluated.Return the value of the last form in the clause that made test true, or the value in the last form in the
elseclause, or#fif the first evaluation of test was true and no forms were evaluated.Examples:
(let ((i #f)) (ensure (= i 1) (by (set! i 1) 123) (else-by (set! i 2) 456) (else (set! i 3) 789))) ⇒ 123 (let ((i #f)) (ensure (= i 2) (by (set! i 1) 123) (else-by (set! i 2) 456) (else (set! i 3) 789))) ⇒ 456 (let ((i #f)) (ensure (= i 3) (by (set! i 1) 123) (else-by (set! i 2) 456) (else (set! i 3) 789))) ⇒ 789
The following bindings are exported by the (parameters)
library and re–exported by the (nausicaa) library.
Return a new parameter object which is bound to a cell containing the value returned by
(converter init). If converter is not specified, the identity function is used instead.The parameter object is a procedure which accepts zero or one argument. When it is called with no argument, the content of the cell bound to this parameter object in the current dynamic environment is returned. When it is called with one argument, the content of the cell bound to this parameter object in the current dynamic environment is set to the result of the call
(converter arg), where arg is the argument passed to the parameter object, and an unspecified value is returned.Examples:
(define radix (make-parameter 10)) (radix) ⇒ 10 (radix 2) (radix) ⇒ 2
The expressions expr1 and expr2 are evaluated in an unspecified order. The value of the expr1 expressions must be parameter objects. For each expr1 expression and in an unspecified order, the local dynamic environment is extended with a binding of the parameter object expr1 to a new cell whose content is the result of the call
(converter val), where val is the value of expr2 and converter is the conversion procedure of the parameter object.The resulting dynamic environment is then used for the evaluation of the forms in body. The result(s) of the
parameterizeform are the result(s) of the body.With reference to the example above:
(radix) ⇒ 2 (parameterize ((radix 16)) (radix)) ⇒ 16 (radix) ⇒ 2 (define (f n) (number->string n (radix))) (f 10) ⇒ "1010" (parameterize ((radix 8)) (f 10)) ⇒ "12"
R6RS Scheme provides the procedure write, which prints
machine–readable representations of lists and other objects. However,
the printed representation does not preserve information about what
parts of the structure are shared, and in the case of self–referential
objects the behavior of write itself is undefined; it is
permitted to go into an infinite loop.
For example, it is possible to have a list within which two or more
members are the same string (in the sense of eq?), but when the
list is written, there is not sufficient information in the
representation to recover the eq? relationship. When the list is
read back in, there will be two or more copies of the string which are
eqv? but not eq?.
As an example of the second problem, the results of evaluating:
(define a (cons 'val1 'val2))
(set-cdr! a a)
(write a)
are undefined.
Write a written representation of obj to the given port. Strings that appear in the written representation are enclosed in doublequotes, and within those strings backslash and doublequote characters are escaped by backslashes. Character objects are written using the
#\notation.
write-with-shared-structureandwrite/ssare bound to the same function.Objects which denote locations rather than values, if they appear at more than one point in the data being written, are preceded by
#N=the first time they are written and replaced by#N#all subsequent times they are written, where N is a natural number used to identify that particular object.If objects which denote locations occur only once in the structure, then
write/ssproduces the same external representation for those objects aswrite.
write/ssterminates in finite time when writing finite data, and produces a finite representation when writing finite data.
write/ssreturns an unspecified value.The port argument may be omitted, in which case it defaults to the value returned by
current-output-port.The optarg is ignored.
The following example shows a cons cell whose cdr contains itself:
(define a (cons 'val1 'val2)) (set-cdr! a a) (write/ss a) -| #1=(val1 . #1#)
Convert the external representations of Scheme objects produced by
write/ssinto scheme objects. Return the next object parsable from the given input port, updating port to point to the first character past the end of the external representation of the object.
read-with-shared-structureandread/ssare bound to the same function.If an end–of–file is encountered in the input before any characters are found that can begin an object, then an end–of–file object is returned. The port remains open, and further attempts to read it (by
read/ssorread) will also return an end–of–file object.If an end–of–file is encountered after the beginning of an object's external representation, but the external representation is incomplete and therefore not parsable, an error is signalled.
The port argument may be omitted, in which case it defaults to the value returned by
current-input-port.It is an error to read from a closed port.
Most operating systems provide a mechanism for passing auxiliary parameters implicitly to child processes. Usually, this mechanism is called “the environment”, and is conceptually a map from string names to string values. The string names are called environment variables.
Some applications rely on environment variables to modify their behavior according to local settings. Also, various established protocols rely on environment variables as a form of interprocess communication.
Return the value of the named environment variable as a string, or
#fif the named environment variable is not found. The name argument is expected to be a string.get-environment-variablemay use locale–setting information to encode the name and decode the value of the environment variable. If the value cannot be decoded, an exception may be raised.(get-environment-variable "PATH") ⇒ "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
Return names and values of all the environment variables as an alist. The same decoding considerations as for
get-environment-variableapply.(get-environment-variables) ⇒ (("PATH" . "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin") ("USERNAME" . "taro"))
The following bindings are exported by the (unimplemented)
library and re–exported by the (nausicaa) library.
The type of condition used to signal unimplemented features. It is defined as:
(define-condition-type &unimplemented &error make-unimplemented-condition unimplemented-condition?)
Raise a composite condition embedding a condition of type
&unimplemented.who is used for the
&whocomponent.message is used for the
&messagecomponent and defaults tofeature not implemented or not available.irritants is used for the
&irritantscomponent.
The (conditions) library defines a set of condition types to be
used by the Nausicaa libraries. It re–exports all the bindings from
the (unimplemented) library.
Condition type used to signal a call to function with the wrong number of arguments; it is derived from
&assertion. It has the following fields:
procname- A Scheme symbol representing the name of the invoked function;
#fif the name is not available.expected- An exact integer representing the expected number of arguments.
given- An exact integer representing the number of arguments used in the offending call.
Constructor and predicate for condition objects of type
&wrong-num-args.
Accessors for the fields of a condition object of type
&wrong-num-args.
Raise a non–continuable exception with compound condition object of type:
&who,&message,&wrong-num-args. it is defined as follows:(define-syntax raise-wrong-num-args (syntax-rules () ((_ ?who ?message ?procname ?expected ?given) (raise (condition (make-who-condition ?who) (make-message-condition ?message) (make-wrong-num-args-condition ?procname ?expected ?given))))))
The (lists) and (lists stx) libraries implement a
collection of functions and macros to manipulate lists; additionally,
(lists low) implements a collection of low level utilities.
These libraries have two purposes: To provide a rich set of functions
and macros and to be a source code repository from which code can be
taken and specialised. For the last purpose, the libraries export
macros that may make little sense in most scenarios (beside the fact
that they are always inlined).
Note The (lists ---) libraries and this section of
documentation are derived from the SRFI-1 document and reference
implementation by Olin Shivers, but they are not compatible
with it. Read carefully this documentation!
Scheme does not properly have a list type, just as the C language does not have a string type. Rather, Scheme has a binary–tuple type, from which one can build binary trees. There is an interpretation of Scheme values that allows one to treat these trees as lists. Further complications ensue from the fact that Scheme allows side–effects to these tuples, raising the possibility of lists of unbounded length, and trees of unbounded depth (that is, circular data structures).
What follows is a classification of concrete values with respect to list as an abstract concept.
It is a special value which can be identified with the predicate
null?. Null is meant to represent empty lists and to be the
terminator for proper lists.
length applied to null returns zero. In light of what follows,
it makes sense to consider null as a list of length zero for all the
list classes: proper, circular, dotted, generalised.
A finite, null–terminated list; more precisely a proper list is defined as: A pair whose cdr is a proper list or null. The opposite of proper is improper; everything that is not null or a proper list, is an improper list.
(a b c)
(32)
We can build a proper list in a single function call with list,
or we can do it in steps using cons and cons*; we can
detect if a list is proper with list?.
An infinite, unterminated list; a circular list is a value such that
cdr applied any number of times always returns a pair. The
opposite of circular is finite.
We can build a list having a circular tail as follows:
(define end (cons 1 '()))
(define tail (cons* 3 2 end))
(define ell (cons* 5 4 tail))
(set-cdr! end tail)
the list structure bound to ‘ell’ looks like this:
cdr
----------------
| |
cdr cdr v cdr cdr |
O----->O----->O----->O----->O--
| | | | |
car| car| car| car| car|
v v v v v
5 4 3 2 1
so that the following happens:
(car ell) ⇒ 5
(cadr ell) ⇒ 4
(caddr ell) ⇒ 3
(cadddr ell) ⇒ 2
(cadddr (cdr ell)) ⇒ 1
(cdddr (cddr ell)) ⇒ tail
(cadddr (cddr ell)) ⇒ 3
(cadddr (cdddr ell)) ⇒ 2
it is impossible to build a circular list without mutating a pair. Notice that the following is not a circular list:
car
--------------------
| |
cdr v cdr cdr cdr | cdr
O----->O----->O----->O----->O----->()
| | | |
car| car| car| car|
v v v v
5 4 3 2
it is a proper list in which the car of the last pair references one of the previous pairs.
We can build a circular list, a ring, with circular-list and we
can detect if a list is a ring or has a circular tail with
circular-list?.
A finite, non–nil terminated list, such as:
(a b c . d)
(x . y)
a dotted list is a value for which there exists an integer n > 0,
such that cdr applied n times yields neither a pair nor
null. This means that, for a dotted list, either null? or
pair? return #t.
Users of the (list ---) libraries should note that dotted lists
are not commonly used, and are considered by many Scheme programmers to
be an ugly artifact of Scheme's lack of a true list type. However,
dotted lists do play a noticeable role in the syntax of Scheme, in the
“rest” parameters used by n–ary lambdas:
(lambda (x y . rest)
---)
Dotted lists are not fully supported by the list libraries; most procedures are defined only on proper lists. The procedures that will also handle circular or dotted lists are specifically marked. While this design decision restricts the domain of possible arguments one can pass to these procedures, it has the benefit of allowing the procedures to catch the error cases where programmers inadvertently pass scalar values to a list procedure by accident (for example, by switching the arguments to a procedure call).
A finite, non–nil terminated list, such as:
(a b c . d)
(x . y)
42
george
a improper list is a value for which there exists an integer n >=
0, such that cdr applied n times yields neither a pair
nor null. This includes non–pair, non–null values (symbols, numbers,
etc.), which are considered to be improper lists of length 0.
Dotted lists are improper lists.
All the functions are exported by the (lists) library, while all
the macros are exported by (lists stx). Each macro has a
corresponding function version, but some functions were not translated
to macros. Macro names always end with the suffix ‘/stx’.
It is an error to pass a circular or dotted list to a procedure not defined to accept such an argument; it is an error to pass a proper or dotted list to a procedure defined for circular lists.
All the functions and macros whose main name ends with ‘!’, are allowed to alter their arguments.
The documentation of this library obeys the following conventions for procedure formals:
The name stands for “eXchanged CONS”, it is like
consbut reverses the arguments. Of utility only as a value to be conveniently passed to higher–order procedures.(cons 1 2) ⇒ (1 . 2) (xcons 1 2) ⇒ (2 . 1)
Return an n–element list, whose elements are all the value fill. If fill is not given, the elements of the list may be arbitrary values.
(make-list 4 'c) ⇒ (c c c c) (make-list 0) ⇒ ()
Copy the whole tree of fell, not only the spine (which is what
list-copydoes).
Return an n–element list. Element i of the list, where 0 <= i < n, is produced by
(init-proc i).The basic variants build the list from element 0 to element n-1. The ‘/reverse’ variants build the lists in reversed order, with element n-1 created first. The ‘/reverse’ variants may be a little faster, especially for long lists.
(list-tabulate 4 values) ⇒ (0 1 2 3)
Return a list containing the elements:
(start start+step ... start+(count-1)*step)The start and step parameters default to
0and1, respectively. count must be a non–negative number. The resulting list is built in reverse, starting from the last element.(iota 5) ⇒ (0 1 2 3 4) (iota 5 0 -0.1) ⇒ (0 -0.1 -0.2 -0.3 -0.4)
Defined as:
(lambda (x) (not (pair? x)))provided as a procedure as it can be useful as the termination condition for list–processing procedures that wish to handle all finite lists, both proper and dotted.
Return
#tif obj is a circular list. The ‘/or-null’ variant returns#talso if obj is null.
Return
#tif obj is a dotted list. The ‘/or-null’ variant returns#talso if obj is null.
Return
#tif all the ell are null or no arguments are given.
Return
#tif at least one of the ell is null. Return#fif no arguments are given.
Return two values being the return values of
and-null?andor-null?applied to the ell arguments.
Determines list equality, given an element–equality procedure item=. Proper list ell1 equals proper list ell2 if:
- They are of the same length.
- Their corresponding elements are equal, according to item=.
If there are no list arguments, or only one list argument: The return value is
#t. If there are two or more list arguments they are compared in couples: First ell0 is compared to ell1, then ell1 is compared to ell2, etc. The iteration over list arguments stops if two list arguments are found different.item= is applied to elements from two list arguments taken with the same order; the first from ell1, the second from ell2, etc. item= must be consistent with
eq?:(eq? x y) ⇒ (elm=? x y)this implies that two lists which are
eq?are alwayslist=?, as well.Examples:
(list=? eq?) ⇒ #t (list=? eq? '(a)) ⇒ #t (list=? = '(1 2 3 4) '(1 2 3 4)) ⇒ #t (list=? = '(1 2 3 4) '(1 9 3 4)) ⇒ #f (list=? = '(1 2 3 4) '(1 2 3 4) '(1 2 3 4)) ⇒ #t (list=? = '(1 2 3 4) '(1 2 3 4 5) '(1 2 3 4)) ⇒ #f (list=? = '()) ⇒ #t (list=? = '() '()) ⇒ #t (list=? = '() '() '()) ⇒ #t
Synonyms for
car,cadr,caddr, ...(third '(a b c d e)) ⇒ c
take-leftreturns the first i elements of list dotted.drop-leftreturns all but the first i elements of list dotted.(take-left '(a b c d e) 2) ⇒ (a b) (drop-left '(a b c d e) 2) ⇒ (c d e)dotted may be any value: a proper, circular, or dotted list:
(take-left '(1 2 3 . d) 2) ⇒ (1 2) (drop-left '(1 2 3 . d) 2) ⇒ (3 . d) (take-left '(1 2 3 . d) 3) ⇒ (1 2 3) (drop-left '(1 2 3 . d) 3) ⇒ dFor a legal i,
take-leftanddrop-leftpartition the list in a manner which can be inverted with append:(append (take-left x i) (drop-left x i)) = x
drop-leftis exactly equivalent to performing icdroperations on dotted; the returned value shares a common tail with dotted.If the argument is a list of non–zero length,
take-leftis guaranteed to return a freshly–allocated list, even in the case where the entire list is taken:(take-left dotted (length+ dotted)).
take-rightreturns the last i elements of dotted.drop-rightreturns all but the last i elements of dotted.(take-right '(a b c d e) 2) ⇒ (d e) (drop-right '(a b c d e) 2) ⇒ (a b c)The returned list may share a common tail with the argument list.
dotted may be any finite list, either proper or dotted:
(take-right '(1 2 3 . d) 2) ⇒ (2 3 . d) (drop-right '(1 2 3 . d) 2) ⇒ (1) (take-right '(1 2 3 . d) 0) ⇒ d (drop-right '(1 2 3 . d) 0) ⇒ (1 2 3)For a legal i,
take-rightanddrop-rightpartition the list in a manner which can be inverted with append:(append (drop-right flist i) (take-right flist i)) = flistThe return value of
take-rightis guaranteed to share a common tail with dotted.If the argument is a list of non–zero length,
drop-rightis guaranteed to return a freshly–allocated list, even in the case where nothing is dropped, e.g.(drop-right dotted 0).
take!anddrop-right!are liketakeanddrop-right, but they are allowed to alter the argument list to produce the result.An error is raised if the length of the list is less than i.
split-atsplits the list dotted at index i, returning a list of the first i elements, and the remaining tail. It is equivalent to:(values (take-left x i) (drop-left x i))
split-at!is allowed to alter the argument list to produce the result.(split-at '(a b c d e f g h) 3) ⇒ (a b c) (d e f g h)
lastreturns the last element of the non–empty, finite list pair.last-pairreturns the last pair in the non–empty, finite list pair.(last '(a b c)) ⇒ c (last-pair '(a b c)) ⇒ (c)
Return the length of the argument, or
#fif circ is a circular list. The length of a proper list is a non–negative integer n such thatcdrapplied n times to the list produces the empty list.
Return a list consisting of the elements of ell followed by the elements of the other list arguments; it is allowed to alter cons cells in the argument lists to construct the result list. The last argument is never altered; the result list shares structure with this parameter.
(append! '(x) '(y)) ⇒ (x y) (append! '(a) '(b c d)) ⇒ (a b c d) (append! '(a (b)) '((c))) ⇒ (a (b) (c)) (append! '(a b) '(c . d)) ⇒ (a b c . d) (append! '() 'a) ⇒ a (append! '(x y)) ⇒ (x y) (append!) ⇒ ()Notice that
appendis implemented by(rnrs base (6)). Pairs and lists
These functions append the elements of their argument together; that is,
concatenatereturns:(apply append list-of-lists)
concatenate!is allowed to alter the arguments to build the result. As withappendandappend!, the last element of the input list may be any value at all.
Return a newly allocated list consisting of the elements of list in reverse order; it is allowed to alter the argument's cons cells to produce the reversed list.
(reverse! '(a b c)) ⇒ (c b a) (reverse! '(a (b c) d (e (f)))) ⇒ ((e (f)) d (b c) a)Notice that
reverseis implemented by(rnrs base (6)). Pairs and lists
append-reversereturns:(append (reverse rev-head) tail)It is provided because it is a common operation; a common list–processing style calls for this exact operation to transfer values accumulated in reverse order onto the front of another list, and because the implementation is significantly more efficient than the simple composition it replaces.
But note that this pattern of iterative computation followed by areversecan frequently be rewritten as a recursion, dispensing with thereverseandappend-reversesteps, and shifting temporary, intermediate storage from the heap to the stack, which is typically a win for reasons of cache locality and eager storage reclamation.
append-reverse!is allowed to alter rev-head's cons cells to construct the result.
The function
zipis defined as:(lambda ells (apply map list ells))while
zip*is defined as:(lambda ells (apply map* list ells))If
zip*is passed n lists, it returns a list as long as the shortest of these lists, each element of which is an n–element list comprised of the corresponding elements from the parameter lists.(zip* '(one two three) '(1 2 3) '(odd even odd even odd even odd even)) ⇒ ((one 1 odd) (two 2 even) (three 3 odd)) (zip* '(1 2 3)) ⇒ ((1) (2) (3))When applying
zip*, at least one of the argument lists must be finite:(zip* '(3 1 4 1) (circular-list #f #t)) ⇒ ((3 #f) (1 #t) (4 #f) (1 #t))
unzip1takes a list of lists, where every list must contain at least one element, and returns a list containing the initial element of each such list. That is, it returns(map car lists).
unzip2takes a list of lists, where every list must contain at least two elements, and returns two values: a list of the first elements, and a list of the second elements.
unzip3does the same for the first three elements of the lists, and so forth.(unzip2 '((1 one) (2 two) (3 three))) ⇒ (1 2 3) (one two three)
pred is a procedure taking as many arguments as there are lists and returning a single value. It is applied element–wise to the elements of the lists, and a count is tallied of the number of elements that produce a true value. This count is returned.
countis “iterative” in that it is guaranteed to apply pred to the list elements in a left–to–right order. The counting stops when the shortest list expires.(count even? '(3 1 4 1 5 9 2 5 6)) ⇒ 3 (count < '(1 2 4 8) '(2 4 6 8 10 12 14 16)) ⇒ 3At least one of the argument lists must be finite:
(count < '(3 1 4 1) (circular-list 1 10)) ⇒ 2
The left–folding operator is the fundamental list iterator, the right–folding operator is the fundamental list recursion operator.
In the single list argument case, for a list of 4 elements, the return value of a left–folding with R6RS style, is computed as with:
(fold-left kons knil ell) ==
(kons (kons (kons (kons knil
(list-ref ell 0))
(list-ref ell 1))
(list-ref ell 2))
(list-ref ell 3))
while with the “traditional” style:
(fold kons knil ell) ==
(kons (list-ref ell 3)
(kons (list-ref ell 2)
(kons (list-ref ell 1)
(kons (list-ref ell 0)
knil))))
the return value of a right–folding with both R6RS style and “traditional” style, is computed as with:
(fold-right kons knil ell) ==
(fold* kons knil ell) ==
(kons (list-ref ell 0)
(kons (list-ref ell 1)
(kons (list-ref ell 2)
(kons (list-ref ell 3)
knil))))
In the multiple list arguments case, for three lists of 4 elements, the return value of a left–folding with R6RS style, is computed as with:
(fold-left kons knil ell0 ell1 ell2) ==
(kons (kons (kons (kons knil
(list-ref ell0 0)
(list-ref ell1 0)
(list-ref ell2 0))
(list-ref ell0 1)
(list-ref ell1 1)
(list-ref ell2 1))
(list-ref ell0 2)
(list-ref ell1 2)
(list-ref ell2 2))
(list-ref ell0 3)
(list-ref ell1 3)
(list-ref ell2 3))
while with the “traditional” style:
(fold kons knil ell0 ell1 ell2) ==
(kons (list-ref ell0 3)
(list-ref ell1 3)
(list-ref ell2 3)
(kons (list-ref ell0 2)
(list-ref ell1 2)
(list-ref ell2 2)
(kons (list-ref ell0 1)
(list-ref ell1 1)
(list-ref ell2 1)
(kons (list-ref ell0 0)
(list-ref ell1 0)
(list-ref ell2 0)
knil))))
the return value of a right–folding with both R6RS style and “traditional” style, is computed as with:
(fold-right kons knil ell0 ell1 ell2) ==
(fold* kons knil ell0 ell1 ell2) ==
(kons (list-ref ell0 0)
(list-ref ell1 0)
(list-ref ell2 0)
(kons (list-ref ell0 1)
(list-ref ell1 1)
(list-ref ell2 1)
(kons (list-ref ell0 2)
(list-ref ell1 2)
(list-ref ell2 2)
(kons (list-ref ell0 3)
(list-ref ell1 3)
(list-ref ell2 3)
knil))))
Left–folding, usage examples for fold in the single list
argument case:
;; add the elements
(fold + 0 '(1 2 3)) ⇒ 6
;; reverse a list
(fold cons '() '(1 2 3)) ⇒ (3 2 1)
;; append in reverse order
(fold cons '(4 5 6) '(3 2 1)) ⇒ (1 2 3 4 5 6)
;; how many symbols?
(fold (lambda (x count)
(if (symbol? x)
(+ count 1)
count))
0
'(a 1 b 2 c 3)) ⇒ 3
Right–folding usage examples for fold* in the single list
argument case:
;; copy the list
(fold* cons '() '(1 2 3)) ⇒ (1 2 3)
;; add elements
(fold* + 0 numbers) ⇒ 45
;; prepend elements
(fold* cons '(4 5 6) '(1 2 3)) ⇒ (1 2 3 4 5 6)
;; filter the even numbers
(fold* (lambda (x l)
(if (even? x)
(cons x l)
l))
'()
'(0 1 2 3 4 5 6 7 8 9))
⇒ (0 2 4 6 8)
Usage examples for fold and fold* in the multiple list
argument case:
(fold (lambda (a b c knil)
(cons (list a b c) knil))
'()
'(1 2 3)
'(10 20 30)
'(100 200 300))
⇒ '((3 30 300)
(2 20 200)
(1 10 100))
(fold* (lambda (a b c knil)
(cons (list a b c)
knil))
'()
'(1 2 3)
'(10 20 30)
'(100 200 300))
⇒ ((1 10 100)
(2 20 200)
(3 30 300))
Exactly as with the fold-left and fold-right functions
defined by R6RS:
Like
fold-leftandfold-right, defined by R6RS, but implemented as syntaxes. These syntaxes exist only for completeness.
Like
fold-leftandfold-right, defined by R6RS, but accept lists of different length and stop at the end of the shortest list. At least one of the list arguments must be finite. The syntaxes may be a little faster than the functions when multiple circ arguments are involved.
The functions fold and fold* are similar to
fold-left* and fold-right* but knil is always the
last argument of the calls to kons.
Left fold the function kons over the elements of the list arguments. Accept lists of different length and stop at the end of the shortest list. At least one of the list arguments must be finite. The syntax may be a little faster than the function when multiple circ arguments are involved.
Right fold the function kons over the elements of the list arguments. Accept lists of different length and stop at the end of the shortest list. At least one of the list arguments must be finite. The syntax may be a little faster than the function when multiple circ arguments are involved.
Like
fold-left*andfold-right*, but stop the folding if the value returned by combine is#f, in which case the return value is#f.
Apply pred to successive couples of elements from circ; return true if all the evaluations of pred were true. The iteration stops at the first
#freturn value from pred.This function is implemented as:
(define (fold-left/pred pred knil ell) (and-fold-left*/stx (lambda (knil item) (and (pred knil item) item)) knil ell))and it can be used to implement predicates for ordering like
<:(fold-left/pred < 0 '(1 2 3 4 5 6)) ⇒ 6 (fold-left/pred < 0 '(1 2 3 -4 5 6)) ⇒ #f
Analogous to
fold, but kons is applied to successive sublists of the lists, rather than successive elements; that is, kons is applied to the pairs making up the lists, giving this (tail) recursion:(pair-fold kons knil ell) == (let ((tail (cdr ell))) (pair-fold kons (kons ell knil) tail)) (pair-fold kons knil '()) == knilFor finite lists, the kons function may reliably apply
set-cdr!to the pairs it is given without altering the sequence of execution.Examples:
(pair-fold (lambda (elm knil) (cons (car elm) knil)) '(999) '(1 2 3)) ⇒ (3 2 1 999) ;;; destructively reverse a list (pair-fold (lambda (pair tail) (set-cdr! pair tail) pair) '() '(0 1 2 3 4 5)) ⇒ (5 4 3 2 1 0)At least one of the list arguments must be finite.
Hold the same relationship with
fold*thatpair-foldholds withfold; obey the recursion:(pair-fold* kons knil lis) == (kons lis (pair-fold* kons knil (cdr lis))) (pair-fold* kons knil '()) == knilexample:
(pair-fold* cons '() '(a b c)) ⇒ ((a b c) (b c) (c))At least one of the list arguments must be finite.
Examples:
(pair-fold* (lambda (elm knil) (cons (car elm) knil)) '(999) '(1 2 3)) ⇒ (1 2 3 999) (pair-fold* (lambda (pair tail) (set-cdr! pair tail) pair) '() '(0 1 2 3 4 5)) ⇒ (0 1 2 3 4 5) (pair-fold* (lambda (a b c knil) (cons (list (car a) (car b) (car c)) knil)) '(999) '(1 2 3) '(10 20 30) '(100 200 300)) ⇒ '((1 10 100) (2 20 200) (3 30 300) 999)
reduceis a variant offold. ridentity should be a “right identity” of the procedure f; that is, for any value x acceptable to f:(f x ridentity) == x
reducehas the following definition:if list = (), return ridentity; otherwise, return (fold f (car list) (cdr list)).in other words, we compute
(fold f ridentity list).Note that ridentity is used only in the empty–list case.
We typically use
reducewhen applying f is expensive and we would like to avoid the extra application incurred whenfoldapplies f to the head of list and the identity value, redundantly producing the same value passed in to f. For example, if f involves searching a file directory or performing a database query, this can be significant. In general, however,foldis useful in many contexts wherereduceis not.Examples:
;; take the max of a list of non-negative integers (reduce max 0 '(1 2 3 4 5 6)) ⇒ 6 (reduce + 0 '(0 1 2 3 4 5 6 7 8 9)) ⇒ 45
reduce*is thefold*variant ofreduce. It obeys the following definition:(reduce* f ridentity '()) == ridentity (reduce* f ridentity '(e1)) == (f e1 ridentity) == e1 (reduce* f ridentity '(e1 e2 ...)) == (f e1 (reduce f ridentity (e2 ...)))in other words, we compute
(fold* f ridentity list).;; append a bunch of lists together (reduce* append '() '((1 2 3) (4 5) (6 7 8 9) (0))) ⇒ (1 2 3 4 5 6 7 8 9 0)
Generate a list from a starting value; return the result. It is is the fundamental recursive list constructor, just as
fold-rightis the fundamental recursive list consumer. It is best described by its basic recursion:(unfold stop? map-to-elm seed-step seed tail-gen) = (if (stop? seed) (tail-gen seed) (cons (map-to-elm seed) (unfold stop? map-to-elm seed-step (seed-step seed) tail-gen)))The arguments are:
- stop?
- Determines when to stop: it is applied to the current seed value, and if the return value is
#t: unfolding stops. If it evaluates to#tat the first invocation: the return value ofunfoldis the return value of tail-gen.- map-to-elm
- Maps each seed value to the corresponding list element. It is applied to the current seed value and must return the value to append to the result list.
- seed-step
- Maps each seed value to next seed value.
- first-seed
- The “state” value for the unfold. It is the first seed value.
- tail-gen
- Applied to the seed value that caused
stop?to return#t, must return the tail of the result list. Defaults to(lambda (x) '()).While
unfoldmay seem a bit abstract to novice functional programmers, it can be used in a number of ways:;; List of squares: 1^2 ... 5^2 (unfold (lambda (x) (> x 5)) (lambda (x) (* x x)) (lambda (x) (+ x 1)) 1) ⇒ (1 4 9 16 25) ;; Copy a proper list. (unfold null-list? car cdr '(1 2 3 4 5)) ⇒ (1 2 3 4 5) ;; Read current input port into a list of values. (unfold eof-object? values (lambda (x) (read)) (read)) ;; Copy a possibly non-proper list: (unfold not-pair? car cdr '(1 2 3 4 . 5) values) ⇒ (1 2 3 4 . 5) ;; Append HEAD onto TAIL: (unfold null-list? car cdr '(1 2 3) (lambda (x) '(4 5 6))) ⇒ (1 2 3 4 5 6)NOTE Interested functional programmers may enjoy noting thatfold-rightandunfoldare in some sense inverses. That is, given operations knull?, kar, kdr, kons, and knil satisfying:(kons (kar x) (kdr x)) = x and (knull? knil) = #tthen:
(fold-right kons knil (unfold knull? kar kdr x)) = xand:
(unfold knull? kar kdr (fold-right kons knil x)) = xThis combinator sometimes is called an “anamorphism”; when an explicit tail-gen procedure is supplied, it is called an “apomorphism”.
Generate a list from a starting value; return the result. It is the fundamental iterative list constructor, just as
foldis the fundamental iterative list consumer. Construct a list with the following loop:(let loop ((seed seed) (ell tail)) (if (stop? seed) ell (loop (seed-step seed) (cons (map-to-elm seed) ell))))Arguments are:
- stop?
- Determine when to stop unfolding.
- map-to-elm
- Map each seed value to the corresponding list element.
- seed-step
- Map each seed value to next seed value.
- first-seed
- The “state” value for the unfold.
- tail
- List terminator; defaults to
'().While
unfold-rightmay seem a bit abstract to novice functional programmers, it can be used in a number of ways:;; List of squares: 1^2 ... 10^2 (unfold-right zero? (lambda (x) (* x x)) (lambda (x) (- x 1)) 5) ⇒ (1 4 9 16 25) ;; Reverse a proper list. (unfold-right null-list? car cdr '(1 2 3 4 5)) ⇒ (5 4 3 2 1) ;; Read current input port into a list of values. (unfold-right eof-object? values (lambda (x) (read)) (read)) ;; Equivalent to: (append-reverse rev-head tail) (unfold-right null-list? car cdr '(3 2 1) '(4 5 6)) ⇒ (1 2 3 4 5 6)NOTE Interested functional programmers may enjoy noting thatfoldandunfold-rightare in some sense inverses. That is, given operations knull?, kar, kdr, kons, and knil satisfying:(kons (kar x) (kdr x)) = x and (knull? knil) = #tthen:
(fold kons knil (unfold-right knull? kar kdr x)) = xand:
(unfold-right knull? kar kdr (fold kons knil x)) = x.This combinator presumably has some pretentious mathematical name; interested readers are invited to communicate it to the author.
This procedure extends the R6RS specification of
mapto allow the arguments to be of unequal length; it terminates when the shortest list runs out of elements.proc is a procedure taking as many arguments as there are list arguments and returning a single value.
mapapplies proc element–wise to the elements of the lists and returns a list of the results, in order. The dynamic order in which proc is applied to the elements of the lists is unspecified.(map* cadr '((a b) (d e) (g h))) ⇒ (b e h) (map* (lambda (n) (expt n n)) '(1 2 3 4 5)) ⇒ (1 4 27 256 3125) (map* + '(1 2 3) '(4 5 6)) ⇒ (5 7 9) (let ((count 0)) (map* (lambda (ignored) (set! count (+ count 1)) count) '(a b))) ⇒ (1 2) or (2 1)At least one of the list arguments must be a finite list:
(map* + '(3 1 4 1) (circular-list 1 0)) ⇒ (4 1 5 1)
A variant of the
mapprocedure that guarantees to apply f across the elements of the circ arguments in a left–to–right order. This is useful for mapping procedures that both have side effects and return useful values.At least one of the list arguments must be finite.
Equivalent to:
(apply append (map f clist1 clist2 ...))and:
(apply append! (map f clist1 clist2 ...))so it makes sense to use these functions only when the list returned by the map process is a list of lists.
Map f over the elements of the lists, just as in the
mapfunction. However, the results of the applications are appended together to make the final result.append-mapusesappendto append the results together;append-map!usesappend!.The dynamic order in which the various applications of f are made is not specified.
Example:
(append-map! (lambda (x) (list x (- x))) '(1 3 8)) ⇒ (1 -1 3 -3 8 -8) (append-map! list '(1 2 3) '(10 20 30)) ⇒ (1 10 2 20 3 30)At least one of the list arguments must be a finite list.
Like
mapandmap/stx, but they are allowed to alter the cons cells of ell0 to construct the result list.The dynamic order in which the various applications of f are made is not specified. In the n–ary case, all the list arguments must have the same number of elements.
Like
map*andmap*!/stx, but they are allowed to alter the cons cells of ell to construct the result list.The dynamic order in which the various applications of f are made is not specified. In the n–ary case, circ ... must have at least as many elements as ell.
Like
map, but only true values are true:(filter-map (lambda (x) (and (number? x) (* x x))) '(a 1 b 3 c 7)) ⇒ (1 9 49)The dynamic order in which the various applications of f are made is not specified. At least one of the list arguments must be finite.
Like
filter-mapandfilter-map/stx, but accept list arguments of different length.
This procedure extends the R6RS specification for
for-eachto allow the arguments to be of unequal length; it terminates when the shortest list runs out of elements.The arguments to
for-each*are like the arguments tomap*, butfor-each*calls proc for its side effects rather than for its values. Unlikemap*,for-each*is guaranteed to call proc on the elements of the lists in order from the first element(s) to the last, and the value returned byfor-each*is unspecified.(let ((v (make-vector 5))) (for-each* (lambda (i) (vector-set! v i (* i i))) '(0 1 2 3 4)) v) ⇒ #(0 1 4 9 16)At least one of the list arguments must be a finite list.
Like
for-each, but f is applied to successive sublists of the argument lists. That is, f is applied to the cons cells of the lists, rather than the lists' elements. These applications occur in left–to–right order.The f procedure may reliably apply
set-cdr!to the pairs it is given without altering the sequence of execution.(pair-for-each (lambda (pair) (display pair) (newline)) '(a b c)) -| (a b c) -| (b c) -| (c)The list arguments must have equal length.
Like
for-each*, but f is applied to successive sublists of the argument lists. That is, f is applied to the cons cells of the lists, rather than the lists' elements. These applications occur in left–to–right order.The f procedure may reliably apply
set-cdr!to the pairs it is given without altering the sequence of execution.(pair-for-each* (lambda (pair) (display pair) (newline)) '(a b c)) -| (a b c) -| (b c) -| (c)At least one of the list arguments must be a finite list.
Return all the elements of ell that satisfy the predicate pred. The list is not disordered: Elements that appear in the result list occur in the same order as they occur in ell. The returned list may share a common tail with ell. The dynamic order in which the various applications of pred are made is not specified.
(filter! even? '(0 7 8 8 43 -4)) ⇒ (0 8 8 -4)
filter!may alter the cons cells in ell to construct the result lists. Notice thatfilteris exported by(rnrs list (6)). List utilities
Partition the elements of ell with predicate pred, and return two values: the list of in–elements and the list of out–elements. The list is not disordered: Elements occur in the result lists in the same order as they occur in ell. The dynamic order in which the various applications of pred are made is not specified. One of the returned lists may share a common tail with ell.
(partition! symbol? '(one 2 3 four five 6)) ⇒ (one four five) (2 3 6)
partition!may alter the cons cells in ell to construct the result lists. Notice thatpartitionis exported by(rnrs list (6)). List utilities
Return ell without the elements that satisfy predicate pred:
(lambda (pred list) (filter (lambda (x) (not (pred x))) list))The list is not disordered: Elements that appear in the result list occur in the same order as they occur in ell. The returned list may share a common tail with ell. The dynamic order in which the various applications of pred are made is not specified.
(remove* even? '(0 7 8 8 43 -4)) ⇒ (7 43)
remove*!is allowed to alter the cons cells in ell to construct the result lists.Notice that
(rnrs lists (6))exportsremove, which accepts a Scheme object as first argument. List utilities
The following procedures search lists for the leftmost elements satisfying some criteria; this means they do not always examine the entire list; thus, there is no efficient way for them to reliably detect and signal an error when passed a dotted or circular list. Here are the general rules describing how these procedures work when applied to different kinds of lists:
Return the longest initial prefix of circ whose elements all satisfy pred.
take-while!is allowed to alter the argument list to produce the result.(take-while even? '(2 18 3 10 22 9)) ⇒ (2 18)
Drop the longest initial prefix of circ whose elements all satisfy pred and return the rest of the list.
(drop-while even? '(2 18 3 10 22 9)) ⇒ (3 10 22 9)The circular–list case may be viewed as “rotating” the list.
spansplits the list into the longest initial prefix whose elements all satisfy pred and the remaining tail.breakinverts the sense of the predicate: The tail commences with the first element of the input list that satisfies the predicate.In other words:
spanfinds the intial span of elements satisfying pred, andbreakbreaks the list at the first element satisfying pred.
spanis equivalent to:(values (take-while pred clist) (drop-while pred clist))
span!andbreak!are allowed to alter the argument list to produce the result.(span even? '(2 18 3 10 22 9)) ⇒ (2 18) (3 10 22 9) (break even? '(3 1 4 1 5 9)) ⇒ (3 1) (4 1 5 9)
Apply the predicate across the lists, returning true if the predicate returns true on any application. If there are N list arguments, then pred must be a procedure taking N arguments and returning a boolean result. If all the list arguments are empty: The return value is
#f.The list arguments of
anymust have the same length;any*accepts lists of different length.
anyapplies pred to the first elements of the list arguments, if this application return true,anyimmediately returns that value; otherwise, it iterates, applying pred to the second elements of the lits arguments and so forth. The iteration stops when a true value is produced or one of the lists runs out of values; in the latter case,anyreturns#f. The application of pred to the last element of the lists is a tail call.Note the difference between
findandany:findreturns the element that satisfied the predicate;anyreturns the true value that the predicate produced.The identifier
anydoes not end with a question mark: This is to indicate that it does not return a simple boolean (#tor#f), but a general value.(any integer? '(a 3 b 2.7)) ⇒ #t (any integer? '(a 3.1 b 2.7)) ⇒ #f (any < '(3 1 4 1 5) '(2 7 1 8 2)) ⇒ #t
Apply the predicate across the lists, returning true if the predicate returns true on every application. If there are N list arguments, then pred must be a procedure taking N arguments and returning a boolean result. If all the list arguments are empty: The return value is
#t.The list arguments of
everymust have the same length;every*accepts lists of different length.
everyapplies pred to the first elements of the list arguments, if this application returns#f,everyimmediately returns#f; otherwise, it iterates, applying pred to the second elements of the list arguments and so forth. The iteration stops when a#fvalue is produced or one of the lists runs out of values. In the latter case,everyreturns the true value produced by its final application of pred. The application of pred to the last element of the lists is a tail call.If one of the list arguments has no elements,
everysimply returns#t.The identifier
everydoes not end with a question mark: This is to indicate that it does not return a simple boolean (#tor#f), but a general value.
Return the index of the leftmost element that satisfies pred. If there are N list arguments, then pred must be a function taking N arguments and returning a boolean result.
The list arguments of
list-indexmust have the same length;list-index*accepts lists of different length.
list-indexapplies pred to the first elements of the list arguments, if this application returns true,list-indeximmediately returns zero; otherwise, it iterates, applying pred to the second elements of the list arguments and so forth. When it finds a tuple of list elements that cause pred to return true, it stops and returns the zero–based index of that position in the lists.The iteration stops when one of the lists runs out of values; in this case,
list-indexreturns#f.(list-index even? '(3 1 4 1 5 9)) ⇒ 2 (list-index < '(3 1 4 1 5 9 2 5 6) '(2 7 1 8 2)) ⇒ 1 (list-index = '(3 1 4 1 5 9 2 5 6) '(2 7 1 8 2)) ⇒ #f
Return the index of the leftmost obj in ell. If obj is not present in ell, return
#f.
Return the first sublist of ell whose car is obj, where the sublists of ell are the non–empty lists returned by
(drop ell i)for i less than the length of ell. If obj does not occur in ell, then#fis returned.Examples:
(member* '(a) '(b (a) c)) ⇒ ((a) c)
member*extends the R6RS definition ofmemberto allow the client to pass in an optional equality procedure item= used to compare keys. item= defaults toequal?.The comparison procedure is used to compare the elements Ei of list to the key obj in this way:
(= x ei) ; list is (E1 ... En)that is, the first argument is always obj, and the second argument is one of the list elements. Thus one can reliably find the first element of list that is greater than five with:
(member* 5 ell <)Note that fully general list searching may be performed with the
find-tailandfindprocedures.
Return the first pair of circ whose car satisfies pred. If no pair does, return
#f.find-tailcan be viewed as a general–predicate variant of themember*function.Examples:
(find-tail even? '(3 1 37 -8 -5 0 0)) ⇒ (-8 -5 0 0) (find-tail even? '(3 1 37 -5)) ⇒ #fIn the circular–list case, this procedure “rotates” the list.
find-tailis essentiallydrop-while, where the sense of the predicate is inverted:find-tailsearches until it finds an element satisfying the predicate;drop-whilesearches until it finds an element that doesn't satisfy the predicate.NOTE Thefindfunction defined by R6RS has an ambiguity in its lookup semantics: iffindreturns#f, we cannot tell (in general) if it found a#felement that satisfied pred, or if it did not find any element at all.In many situations, this ambiguity cannot arise: either the list being searched is known not to contain any
#felements, or the list is guaranteed to have an element satisfying pred.However, in cases where this ambiguity can arise, we should use
find-tailinstead offind,find-tailhas no such ambiguity.
Use the comparison procedure item= (which defaults to
equal?) to find all the elements of ell that are equal to obj, and delete them from ell. The dynamic order in which the various applications of item= are made is not specified.The list is not disordered: Elements that appear in the result list occur in the same order as they occur in ell. The result may share a common tail with ell.
Note that fully general element deletion can be performed with the
removeandremove!procedures.The comparison procedure is used as
(item= obj Ei), that is: obj is always the first argument and a list element is always the second argument. The comparison procedure will be used to compare each element of list exactly once; the order in which it is applied to the various Ei is not specified. Thus, one can reliably remove all the numbers greater than 5 from a list with(delete 5 ell <).
delete!is allowed to alter the cons cells in ell to construct the result.
Remove duplicate elements from ell. If there are multiple equal elements in ell, the result list only contains the first or leftmost of these elements in the result. The order of these surviving elements is the same as in the original list:
delete-duplicatesdoes not disorder the list (hence it is useful for “cleaning up” association lists).The item= parameter is used to compare the elements of the list; it defaults to
equal?. If x comes before y in ell, then the comparison is performed as(item= x y). The comparison procedure will be used to compare each pair of elements in ell no more than once; the order in which it is applied to the various pairs is not specified.The list argument may share a common tail with the returned list.
delete-duplicates!is allowed to alter the cons cells i