Go to the first, previous, next, last section, table of contents.

Miscellaneous Datatypes

Booleans

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

variable+: false
variable+: true
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.

procedure: boolean? object
Returns #t if object is either #t or #f; otherwise returns #f.

(boolean? #f)                           =>  #t
(boolean? 0)                            =>  #f

procedure: not object
procedure+: false? object
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.

Symbols

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.

procedure: symbol? object
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.

procedure+: intern string
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

Cells are data structures similar to pairs except that they have only one element. They are useful for managing state.

procedure+: cell? object
Returns #t if object is a cell; otherwise returns #f.

procedure+: make-cell object
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).

Records

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.

procedure+: record? object
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)

Promises

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.

procedure: force promise
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

procedure+: promise? object
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):

  • Calling force on an object that is not a promise may simply return the object.
  • It may be the case that there is no means by which a promise can be operationally distinguished from its forced value. That is, expressions like the following may evaluate to either #t or #f, depending on the implementation:
    (eqv? (delay 1) 1)              =>  unspecified
    (pair? (delay (cons 1 2)))      =>  unspecified
    
  • Some implementations will implement "implicit forcing", where the value of a promise is forced by primitive procedures like car and +:
    (+ (delay (* 3 7)) 13)          =>  34
    

Streams

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.

procedure+: stream object ...
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)

procedure+: list->stream list
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-car stream
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-cdr stream
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

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?.

procedure+: weak-pair? object
Returns #t if object is a weak pair; otherwise returns #f.

procedure+: weak-cons car cdr
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 first, previous, next, last section, table of contents.