Go to the previous, next section.
The boolean objects are true and false. The boolean constant true is written as `#t', and the boolean constant false is written as `#f'.
The primary use for boolean objects is in the conditional expressions
if
, cond
, and
, and or
; the behavior of these
expressions is determined by whether objects are true or false. These
expressions count only #f
as false. They count everything else,
including #t
, pairs, symbols, numbers, strings, vectors, and
procedures as true (but see section True and False).
Programmers accustomed to other dialects of Lisp should note that Scheme
distinguishes #f
and the empty list from the symbol nil
.
Similarly, #t
is distinguished from the symbol t
. In
fact, the boolean objects (and the empty list) are not symbols at all.
Boolean constants evaluate to themselves, so you don't need to quote them.
#t => #t #f => #f '#f => #f t error--> Unbound variable
These variables are bound to the objects #f
and #t
respectively. The compiler, given some standard declarations, replaces
references to these variables with their respective values.
Note that the symbol true
is not equivalent to #t
, and the
symbol false
is not equivalent to #f
.
Returns #t
if object is either #t
or #f
;
otherwise returns #f
.
(boolean? #f) => #t (boolean? 0) => #f
These procedures return #t
if object is false; otherwise
they return #f
. In other words they invert boolean
values. These two procedures have identical semantics; their names are
different to give different connotations to the test.
(not #t) => #f (not 3) => #f (not (list 3)) => #f (not #f) => #t
procedure+: boolean=? obj1 obj2
This predicate is true iff obj1 and obj2 are either both true or both false.
procedure+: boolean/and object ...
This procedure returns #t
if none of its arguments are #f
.
Otherwise it returns #f
.
procedure+: boolean/or object ...
This procedure returns #f
if all of its arguments are #f
.
Otherwise it returns #t
.
MIT Scheme provides two types of symbols: interned and
uninterned. Interned symbols are far more common than uninterned
symbols, and there are more ways to create them. Interned symbols have
an external representation that is recognized by the procedure
read
; uninterned symbols do not.(11)
Interned symbols have an extremely useful property: any two interned
symbols whose names are the same, in the sense of string=?
, are
the same object (i.e. they are eqv?
to one another). The term
interned refers to the process of interning by which this is
accomplished. Uninterned symbols do not share this property.
The names of interned symbols are not distinguished by their alphabetic
case. Because of this, MIT Scheme converts all alphabetic
characters in the name of an interned symbol to a specific case (lower
case) when the symbol is created. When the name of an interned symbol
is referenced (using symbol->string
) or written (using
write
) it appears in this case. It is a bad idea to depend on
the name being lower case. In fact, it is preferable to take this one
step further: don't depend on the name of a symbol being in a uniform
case.
The rules for writing an interned symbol are the same as the rules for
writing an identifier (see section Identifiers). Any interned symbol that
has been returned as part of a literal expression, or read using the
read
procedure and subsequently written out using the
write
procedure, will read back in as the identical symbol (in
the sense of eqv?
).
Usually it is also true that reading in an interned symbol that was
previously written out produces the same symbol. An exception are
symbols created by the procedures string->symbol
and
intern
; they can create symbols for which this write/read
invariance may not hold because the symbols' names contain special
characters or letters in the non-standard case.(12)
The external representation for uninterned symbols is special, to
distinguish them from interned symbols and prevent them from being
recognized by the read
procedure:
(string->uninterned-symbol "foo") => #[uninterned-symbol 30 foo]
In this section, the procedures that return symbols as values will either always return interned symbols, or always return uninterned symbols. The procedures that accept symbols as arguments will always accept either interned or uninterned symbols, and do not distinguish the two.
Returns #t
if object is a symbol, otherwise returns
#f
.
(symbol? 'foo) => #t (symbol? (car '(a b))) => #t (symbol? "bar") => #f
procedure: symbol->string symbol
Returns the name of symbol as a string. If symbol was
returned by string->symbol
, the value of this procedure will be
identical (in the sense of string=?
) to the string that was
passed to string->symbol
. It is an error to apply mutation
procedures such as string-set!
to strings returned by this
procedure.
(symbol->string 'flying-fish) => "flying-fish" (symbol->string 'Martin) => "martin" (symbol->string (string->symbol "Malvina")) => "Malvina"
Note that two distinct uninterned symbols can have the same name.
Returns the interned symbol whose name is string. Converts string to the standard alphabetic case before generating the symbol. This is the preferred way to create interned symbols, as it guarantees the following independent of which case the implementation uses for symbols' names:
(eq? 'bitBlt (intern "bitBlt")) => #t
The user should take care that string obeys the rules for identifiers (see section Identifiers), otherwise the resulting symbol cannot be read as itself.
procedure: string->symbol string
Returns the interned symbol whose name is string. Although you
can use this procedure to create symbols with names containing special
characters or lowercase letters, it's usually a bad idea to create such
symbols because they cannot be read as themselves. See
symbol->string
.
(eq? 'mISSISSIppi 'mississippi) => #t (string->symbol "mISSISSIppi") => the symbol with the name "mISSISSIppi" (eq? 'bitBlt (string->symbol "bitBlt")) => #f (eq? 'JollyWog (string->symbol (symbol->string 'JollyWog))) => #t (string=? "K. Harper, M.D." (symbol->string (string->symbol "K. Harper, M.D."))) => #t
procedure+: string->uninterned-symbol string
Returns a newly allocated uninterned symbol whose name is string. It is unimportant what case or characters are used in string.
Note: this is the fastest way to make a symbol.
procedure+: generate-uninterned-symbol [object]
Returns a newly allocated uninterned symbol that is guaranteed not to be
eqv?
to any other object in the Scheme system. The symbol's name
consists of a string (initially "G"
) followed by an integer that
is incremented on every call (the integer is initially 0). The optional
object can be an integer or a symbol. If object is a
symbol, the string prefix of all subsequently generated symbol names
will be that symbol's name. If object is an integer, the integer
suffix of all subsequently generated symbol names will start counting
from that value.
(generate-uninterned-symbol) => #[uninterned-symbol 31 g0] (generate-uninterned-symbol) => #[uninterned-symbol 32 g1] (generate-uninterned-symbol 'this) => #[uninterned-symbol 33 this2] (generate-uninterned-symbol) => #[uninterned-symbol 34 this3] (generate-uninterned-symbol 100) => #[uninterned-symbol 35 this100] (generate-uninterned-symbol) => #[uninterned-symbol 36 this101]
procedure+: symbol-append symbol ...
Returns the interned symbol whose name is formed by concatenating the names of the given symbols. This procedure preserves the case of the names of its arguments, so if one or more of the arguments' names has non-standard case, the result will also have non-standard case.
(symbol-append 'foo- 'bar) => foo-bar ;; the arguments may be uninterned: (symbol-append 'foo- (string->uninterned-symbol "baz")) => foo-baz ;; the result has the same case as the arguments: (symbol-append 'foo- (string->symbol "BAZ")) => foo-BAZ
procedure+: symbol-hash symbol
Returns a hash number for symbol, which is computed by calling
string-hash
on symbol's name.
Cells are data structures similar to pairs except that they have only one element. They are useful for managing state.
Returns #t
if object is a cell; otherwise returns
#f
.
Returns a newly allocated cell whose contents is object.
procedure+: cell-contents cell
Returns the current contents of cell.
procedure+: set-cell-contents! cell object
Alters the contents of cell to be object. Returns an unspecified value.
procedure+: bind-cell-contents! cell object thunk
Alters the contents of cell to be object, calls thunk with no arguments, then restores the original contents of cell and returns the value returned by thunk. This is completely equivalent to fluid binding of a variable, including the behavior when continuations are used (see section Fluid Binding).
MIT Scheme provides a record abstraction, which is a simple and
flexible mechanism for building structures with named components.
Records can be defined and accessed using the procedures defined in this
section. A less flexible but more concise way to manipulate records is
to use the define-structure
special form (see section Structure Definitions).
procedure+: make-record-type type-name field-names
Returns a record-type descriptor, a value representing a new data type, disjoint from all others. The type-name argument must be a string, but is only used for debugging purposes (such as the printed representation of a record of the new type). The field-names argument is a list of symbols naming the fields of a record of the new type. It is an error if the list contains any duplicates. It is unspecified how record-type descriptors are represented.
procedure+: record-constructor record-type [field-names]
Returns a procedure for constructing new members of the type represented
by record-type. The returned procedure accepts exactly as many
arguments as there are symbols in the given list, field-names;
these are used, in order, as the initial values of those fields in a new
record, which is returned by the constructor procedure. The values of
any fields not named in the list of field-names are unspecified.
The field-names argument defaults to the list of field-names in
the call to make-record-type
that created the type represented by
record-type; if the field-names argument is provided, it is
an error if it contains any duplicates or any symbols not in the default
list.
procedure+: record-predicate record-type
Returns a procedure for testing membership in the type represented by
record-type. The returned procedure accepts exactly one argument
and returns #t
if the argument is a member of the indicated
record type; it returns #f
otherwise.
procedure+: record-accessor record-type field-name
Returns a procedure for reading the value of a particular field of a
member of the type represented by record-type. The returned
procedure accepts exactly one argument which must be a record of the
appropriate type; it returns the current value of the field named by the
symbol field-name in that record. The symbol field-name
must be a member of the list of field names in the call to
make-record-type
that created the type represented by
record-type.
procedure+: record-modifier record-type field-name
Returns a procedure for writing the value of a particular field of a
member of the type represented by record-type. The returned
procedure accepts exactly two arguments: first, a record of the
appropriate type, and second, an arbitrary Scheme value; it modifies the
field named by the symbol field-name in that record to contain the
given value. The returned value of the modifier procedure is
unspecified. The symbol field-name must be a member of the list
of field names in the call to make-record-type
that created the
type represented by record-type.
For compatibility with old code, record-updater
is a synonym for
this procedure.
Returns #t
if object is a record of any type and #f
otherwise. Note that record?
may be true of any Scheme value; of
course, if it returns #t
for some particular value, then
record-type-descriptor
is applicable to that value and returns an
appropriate descriptor.
procedure+: record-type-descriptor record
Returns the record-type descriptor representing the type of
record. That is, for example, if the returned descriptor were
passed to record-predicate
, the resulting predicate would return
#t
when passed record. Note that it is not necessarily the
case that the returned descriptor is the one that was passed to
record-constructor
in the call that created the constructor
procedure that created record.
procedure+: record-type? object
Returns #t
if object is a record-type descriptor; otherwise
returns #f
.
procedure+: record-type-name record-type
Returns the type name associated with the type represented by
record-type. The returned value is eqv?
to the
type-name argument given in the call to make-record-type
that created the type represented by record-type.
procedure+: record-type-field-names record-type
Returns a list of the symbols naming the fields in members of the type
represented by record-type. The returned value is equal?
to the field-names argument given in the call to
make-record-type
that created the type represented by
record-type.(13)
special form: delay expression
The delay
construct is used together with the procedure
force
to 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 the force
procedure) to evaluate expression and deliver the resulting value.
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 without any recomputation.
(force (delay (+ 1 2))) => 3 (let ((p (delay (+ 1 2)))) (list (force p) (force p))) => (3 3) (define head car) (define tail (lambda (stream) (force (cdr stream)))) (define a-stream (letrec ((next (lambda (n) (cons n (delay (next (+ n 1))))))) (next 0))) (head (tail (tail a-stream))) => 2
Returns #t
if object is a promise; otherwise returns
#f
.
procedure+: promise-forced? promise
Returns #t
if promise has been forced and its value cached;
otherwise returns #f
.
procedure+: promise-value promise
If promise has been forced and its value cached, this procedure returns the cached value. Otherwise, an error is signalled.
force
and delay
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
the value of a promise is computed at most once.
(define count 0) (define p (delay (begin (set! count (+ count 1)) (* x 3)))) (define x 5) count => 0 p => #[promise 54] (force p) => 15 p => #[promise 54] count => 1 (force p) => 15 count => 1
Here is a possible implementation of delay
and force
. We
define the expression
(delay expression)
to have the same meaning as the procedure call
(make-promise (lambda () expression))
where make-promise
is defined as follows:
(define make-promise (lambda (proc) (let ((already-run? #f) (result #f)) (lambda () (cond ((not already-run?) (set! result (proc)) (set! already-run? #t))) result))))
Promises are implemented here as procedures of no arguments, and
force
simply calls its argument.
(define force (lambda (promise) (promise)))
Various extensions to this semantics of delay
and force
are supported in some implementations (none of these are currently
supported in MIT Scheme):
force
on an object that is not a promise may simply
return the object.
#t
or #f
,
depending on the implementation:
(eqv? (delay 1) 1) => unspecified (pair? (delay (cons 1 2))) => unspecified
car
and
+
:
(+ (delay (* 3 7)) 13) => 34
In addition to promises, MIT Scheme supports a higher-level abstraction called streams. Streams are similar to lists, except that the tail of a stream is not computed until it is referred to. This allows streams to be used to represent infinitely long lists.
Returns a newly allocated stream whose elements are the arguments. Note
that the expression (stream)
returns the empty stream, or
end-of-stream marker.(14)
Returns a newly allocated stream whose elements are the elements of
list. Equivalent to (apply stream list)
.
procedure+: stream->list stream
Returns a newly allocated list whose elements are the elements of stream. If stream has infinite length this procedure will not terminate. This could have been defined by
(define (stream->list stream) (if (stream-null? stream) '() (cons (stream-car stream) (stream->list (stream-cdr stream)))))
special form+: cons-stream object expression
Returns a newly allocated stream pair. Equivalent to (cons
object (delay expression))
.
procedure+: stream-pair? object
Returns #t
if object is a pair whose cdr contains a
promise. Otherwise returns #f
. This could have been defined by
(define (stream-pair? object) (and (pair? object) (promise? (cdr object))))
procedure+: stream-first stream
Returns the first element in stream. stream-car
is
equivalent to car
.(15) stream-first
is a synonym for
stream-car
.
procedure+: stream-rest stream
Returns the first tail of stream. Equivalent to (force
(cdr stream))
.(16) stream-rest
is a synonym for
stream-cdr
.
procedure+: stream-null? stream
Returns #t
if stream is the end-of-stream marker;
otherwise returns #f
. This is equivalent to null?
, but
should be used whenever testing for the end of a
stream.(17)
procedure+: stream-length stream
Returns the number of elements in stream. If stream has an infinite number of elements this procedure will not terminate. Note that this procedure forces all of the promises that comprise stream.
procedure+: stream-ref stream k
Returns the element of stream that is indexed by k; that is, the kth element. K must be an exact non-negative integer strictly less than the length of stream.
procedure+: stream-tail stream k
Returns the tail of stream that is indexed by k; that is,
the kth tail. This is equivalent to performing stream-cdr
k times. K must be an exact non-negative integer strictly
less than the length of stream.
procedure+: stream-map stream procedure
Returns a newly allocated stream, each element being the result of invoking procedure with the corresponding element of stream as its argument. Procedure must be a procedure of one argument.
Weak pairs are a mechanism for building data structures that point at objects without protecting them from garbage collection. The car of a weak pair holds its pointer weakly, while the cdr holds its pointer in the normal way. If the object in the car of a weak pair is not held normally by any other data structure, it will be garbage-collected.
Note: weak pairs are not pairs; that is, they do not satisfy the
predicate pair?
.
Returns #t
if object is a weak pair; otherwise returns
#f
.
Allocates and returns a new weak pair, with components car and cdr. The car component is held weakly.
procedure+: weak-pair/car? weak-pair
This predicate returns #f
if the car of weak-pair has been
garbage-collected; otherwise returns #t
. In other words, it is
true if weak-pair has a valid car component.
procedure+: weak-car weak-pair
Returns the car component of weak-pair. If the car component has
been garbage-collected, this operation returns #f
, but it can
also return #f
if that is the value that was stored in the car.
Normally, weak-pair/car?
is used to determine if weak-car
would return a valid value. An obvious way of doing this would be:
(if (weak-pair/car? x) (weak-car x) ...)
However, since a garbage collection could occur between the call to
weak-pair/car?
and weak-car
, this would not always work
correctly. Instead, the following should be used, which always works:
(or (weak-car x) (and (not (weak-pair/car? x)) ...))
The reason that the latter expression works is that weak-car
returns #f
in just two instances: when the car component is
#f
, and when the car component has been garbage-collected. In
the former case, if a garbage collection happens between the two calls,
it won't matter, because #f
will never be garbage-collected. And
in the latter case, it also won't matter, because the car component no
longer exists and cannot be affected by the garbage collector.
procedure+: weak-set-car! weak-pair object
Sets the car component of weak-pair to object and returns an unspecified result.
procedure+: weak-cdr weak-pair
Returns the cdr component of weak-cdr.
procedure+: weak-set-cdr! weak-pair object
Sets the cdr component of weak-pair to object and returns an unspecified result.
Go to the previous, next section.