Promises - MIT/GNU Scheme 7.7.91
Description: MIT/GNU Scheme 7.7.91

Next: Streams, Previous: Records, Up: Miscellaneous Datatypes

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