MIT/GNU Scheme Reference: Miscellaneous Datatypes

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

# 10. Miscellaneous Datatypes

> 10.1 Booleans |  |
> 10.2 Symbols |  |
> 10.3 Cells |  |
> 10.4 Records |  |
> 10.5 Promises |  |
> 10.6 Streams |  |
> 10.7 Weak Pairs |  |

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 10.1 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 1.2.5 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 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`.

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

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 10.2 Symbols

MIT/GNU 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.(9)

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/GNU 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 1.3.3 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.(10)

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 1.3.3 Identifiers), otherwise the resulting symbol cannot be read as itself.

procedure: intern-soft string
  Returns the interned symbol whose name is string. Converts string to the standard alphabetic case before generating the symbol. If no such interned symbol exists, returns `#f`.

  This is exactly like `intern`, except that it will not create an interned symbol, but only returns symbols that already exist.

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 to be different from any other object. The symbol's name consists of a prefix string followed by the (exact non-negative integer) value of an internal counter. The counter is initially zero, and is incremented after each call to this procedure.

  The optional argument object is used to control how the symbol is generated. It may take one of the following values:

  - If object is omitted or `#f`, the prefix is `"G"`.

  - If object is an exact non-negative integer, the internal counter is set to that integer prior to generating the result.

  - If object is a string, it is used as the prefix.

  - If object is a symbol, its name is used as the prefix.

      (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]

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. The hash number is an exact non-negative integer.

procedure: symbol-hash-mod symbol modulus
  Modulus must be an exact positive integer. Equivalent to

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

procedure: symbol<? symbol1 symbol2
  This procedure computes a total order on symbols. It is equivalent to

      (string<? (symbol->string symbol1)
                (symbol->string symbol2))

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 10.3 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 dynamic binding of a variable, including the behavior when continuations are used (see section 2.3 Dynamic Binding).

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 10.4 Records

MIT/GNU 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 2.10 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-keyword-constructor record-type
  Returns a procedure for constructing new members of the type represented by record-type. The returned procedure accepts arguments in a keyword list, which is an alternating sequence of names and values. In other words, the number of arguments must be a multiple of two, and every other argument, starting with the first argument, must be a symbol that is one of the field names for record-type.

  The returned procedure may be called with a keyword list that contains multiple instances of the same keyword. In this case, the leftmost instance is used and the other instances are ignored. This allows keyword lists to be accumulated using `cons` or `cons*`, and new bindings added to the front of the list override old bindings at the end.

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.

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.(11)

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 10.5 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/GNU 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

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 10.6 Streams

In addition to promises, MIT/GNU 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.

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`. `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))`. `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.

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-head stream k
  Returns the first k elements of stream as a list. 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 procedure stream stream ...
  Returns a newly allocated stream, each element being the result of invoking procedure with the corresponding elements of the streams as its arguments.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 10.7 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.

[ << ] | [ >> ] |  |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

This document was generated by Chris Hanson on September, 19 2003 using texi2html