Promises (MIT/GNU Scheme 11.0.90)

Next: Streams, Previous: Records, Up: Miscellaneous Datatypes [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

Next: Streams, Previous: Records, Up: Miscellaneous Datatypes [Contents][Index]