Go to the first, previous, next, last section, table of contents.
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
#f
and #t
respectively. The compiler, given the usual-integrations
declaration, 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
.
#t
if object is either #t
or #f
;
otherwise returns #f
.
(boolean? #f) => #t (boolean? 0) => #f
#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
#t
if none of its arguments are #f
.
Otherwise it returns #f
.
#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 eq?
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 eq?
).
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.
#t
if object is a symbol, otherwise returns
#f
.
(symbol? 'foo) => #t (symbol? (car '(a b))) => #t (symbol? "bar") => #f
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.
(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.
#f
.
This is exactly like intern
, except that it will not create an
interned symbol, but only returns symbols that already exist.
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
Note: this is the fastest way to make a symbol.
The optional argument object is used to control how the symbol is generated. It may take one of the following values:
#f
, the prefix is "G"
.
(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 G3] (generate-uninterned-symbol 100) => #[uninterned-symbol 35 G100] (generate-uninterned-symbol) => #[uninterned-symbol 36 G101]
(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
string-hash
on symbol's name. The hash number is an exact
non-negative integer.
(modulo (symbol-hash symbol) modulus)
This procedure is provided for convenience in constructing hash tables.
However, it is normally preferable to use make-eq-hash-table
to
build hash tables keyed by symbols, because eq?
hash tables are
much faster.
(string<? (symbol->string symbol1) (symbol->string symbol2))
Cells are data structures similar to pairs except that they have only one element. They are useful for managing state.
#t
if object is a cell; otherwise returns
#f
.
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).
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.
#t
if the argument is a member of the indicated
record type; it returns #f
otherwise.
make-record-type
that created the type represented by
record-type.
make-record-type
that created the
type represented by record-type.
#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.
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.
#t
if object is a record-type descriptor; otherwise
returns #f
.
eqv?
to the
type-name argument given in the call to make-record-type
that created the type represented by record-type.
equal?
to the field-names argument given in the call to
make-record-type
that created the type represented by
record-type.(13)
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.
(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
#t
if object is a promise; otherwise returns
#f
.
#t
if promise has been forced and its value cached;
otherwise returns #f
.
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.
(stream)
returns the empty stream, or
end-of-stream marker.
(apply stream list)
.
(define (stream->list stream) (if (stream-null? stream) '() (cons (stream-car stream) (stream->list (stream-cdr stream)))))
(cons
object (delay expression))
.
#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))))
stream-car
is
equivalent to car
. stream-first
is a synonym for
stream-car
.
(force (cdr
stream))
. stream-rest
is a synonym for stream-cdr
.
#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.
stream-cdr
k times. K must be an exact non-negative integer strictly
less than the length of stream.
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?
.
#t
if object is a weak pair; otherwise returns
#f
.
#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.
#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.
Go to the first, previous, next, last section, table of contents.