Next: , 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):