(delay ⟨expression⟩) ¶Semantics: 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 can be asked
(by the force procedure) to evaluate ⟨expression⟩, and
deliver the resulting value. The effect of ⟨expression⟩ returning
multiple values is unspecified.
(delay-force ⟨expression⟩) ¶Semantics: The expression ‘(delay-force ⟨expression⟩)’ is
conceptually similar to ‘(delay (force ⟨expression⟩))’, with the
difference that forcing the result of delay-force will in effect
result in a tail call to ‘(force ⟨expression⟩)’, while forcing
the result of ‘(delay (force ⟨expression⟩))’ might not. Thus
iterative lazy algorithms that might result in a long series of chains
of delay and force can be rewritten using delay-force
to prevent consuming unbounded space during evaluation.
(force promise) ¶The force procedure forces the value of a promise created by
delay, delay-force, or make-promise. If no value has
been computed for the promise, then a value is computed and returned.
The value of the promise must be cached (or “memoized”) so that
if it is forced a second time, the previously computed value is
returned. Consequently, a delayed expression is evaluated using the
parameter values and exception handler of the call to force which
first requested its value. If promise is not a promise, it may be
returned unchanged.
(force (delay (+ 1 2))) ⇒ 3
(let ((p (delay (+ 1 2)))) (list (force p) (force p))) ⇒ (3 3)
(define integers (letrec ((next (lambda (n) (delay (cons n (next (+ n 1))))))) (next 0))) (define head (lambda (stream) (car (force stream)))) (define tail (lambda (stream) (cdr (force stream)))) (head (tail (tail integers))) ⇒ 2
The following example is a mechanical transformation of a lazy
stream-filtering algorithm into Scheme. Each call to a constructor is
wrapped in delay, and each argument passed to a deconstructor is
wrapped in force. The use of ‘(delay-force …)’
instead of ‘(delay (force …))’ around the body of the
procedure ensures that an ever-growing sequence of pending promises does
not exhaust available storage, because force will in effect force
such sequences iteratively.
(define (stream-filter p? s) (delay-force (if (null? (force s)) (delay '()) (let ((h (car (force s))) (t (cdr (force s)))) (if (p? h) (delay (cons h (stream-filter p? t))) (stream-filter p? t)))))) (head (tail (tail (stream-filter odd? integers)))) ⇒ 5
The following examples are not intended to illustrate good programming
style, as delay, force, and delay-force are mainly
intended for programs written in the functional style. However, they do
illustrate the property that only one value is computed for a promise,
no matter how many times it is forced.
(define count 0) (define p (delay (begin (set! count (+ count 1)) (if (> count x) count (force p))))) (define x 5) p ⇒ a promise (force p) ⇒ 6 p ⇒ a promise, still (begin (set! x 10) (force p)) ⇒ 6
Various extensions to this semantics of delay, force, and
delay-force are supported in some implementations:
force on an object that is not a promise may simply
return the object.
#t or to #f,
depending on the implementation:
(eqv? (delay 1) 1) ⇒ unspecified
(pair? (delay (cons 1 2))) ⇒ unspecified
cdr and *. However,
procedures that operate uniformly on their arguments, like list,
must not force them.
(+ (delay (* 3 7)) 13) ⇒ unspecified
(car (list (delay (* 3 7)) 13)) ⇒ a promise
(promise? obj) ¶The promise? procedure returns #t if its argument is a
promise, and #f otherwise. Note that promises are not
necessarily disjoint from other Scheme types such as procedures.
(make-promise obj) ¶The make-promise procedure returns a promise which, when forced,
will return obj. It is similar to delay, but does not
delay its argument: it is a procedure rather than syntax. If obj
is already a promise, it is returned.