racket/collects/scribblings/reference/promise.scrbl
Eli Barzilay b1f5b0652c new kinds of promises
svn: r16807
2009-11-16 11:06:47 +00:00

149 lines
5.7 KiB
Racket

#lang scribble/doc
@(require "mz.ss"
(for-label scheme/promise))
@title{Delayed Evaluation}
@note-lib[scheme/promise]
A @deftech{promise} encapsulates an expression to be evaluated on
demand via @scheme[force]. After a promise has been @scheme[force]d,
every later @scheme[force] of the promise produces the same result.
This module provides this functionality, and extends it to additional
kinds of promises with various evaluation strategies.
@defproc[(promise? [v any/c]) boolean?]{
Returns @scheme[#t] if @scheme[v] is a promise, @scheme[#f]
otherwise.}
@defform[(delay body ...+)]{
Creates a promise that, when @scheme[force]d, evaluates the
@scheme[body]s to produce its value. The result is then cached, so
further uses of @scheme[force] produce the cached value immediately.
This includes multiple values and exceptions.}
@defform[(lazy body ...+)]{
Like @scheme[delay], if the last @scheme[body] produces a promise when
forced, then this promise is @scheme[force]d too to obtain a value.
In other words, this form creates a composable promise, where the
computation of its body is ``attached'' to the computation of the
following promise and a single @scheme[force] iterates through the
whole chain, tail-calling each step.
Note that the last @scheme[body] of this form must produce a single
value --- but this value can itself be a @scheme[delay] promise that
returns multiple values.
This form useful for implementing lazy libraries and languages, where
tail-calls can be wrapped in a promise.}
@defproc[(force [v any/c]) any]{
If @scheme[v] is a promise, then the promise is forced to obtain a
value. If the promise has not been forced before, then the result is
recorded in the promise so that future @scheme[force]s on the promise
produce the same value (or values). If forcing the promise raises an
exception, then the exception is similarly recorded so that forcing
the promise will raise the same exception every time.
If @scheme[v] is @scheme[force]d again before the original call to
@scheme[force] returns, then the @exnraise[exn:fail].
Additional kinds of promises are also forced via @scheme[force]. See
below for further details.
If @scheme[v] is not a promise, then it is returned as the result.}
@defproc[(promise-forced? [promise promise?]) boolean?]{
Returns @scheme[#t] if @scheme[promise] has been forced.}
@defproc[(promise-running? [promise promise?]) boolean?]{
Returns @scheme[#t] if @scheme[promise] is currently being forced.
(Note that a promise can be either running or forced but not both.)}
@section{Additional Promise Kinds}
@defform[(delay/name body ...+)]{
Creates a ``call by name'' promise, that is similar to
@scheme[delay]-promises, except that the resulting value is not
cached. It is essentially a thunk, wrapped in a way that
@scheme[force] recognizes. Note that if a @scheme[delay/name] promise
forces itself, no exception is raised.
@; TODO: clarify that the point is that code that is written using
@; `force', can be used with these promises too.
Note that this promise is never considered ``running'' or ``forced''
in the sense of @scheme[promise-running?] and
@scheme[promise-forced?].}
@defform[(delay/sync body ...+)]{
Conventional promises are not useful when multiple threads attempt to
force them: when a promise is running, any additional threads that
@scheme[force] it will get an exception. @scheme[delay/sync] is
useful for such cases: if a second thread attempts to @scheme[force]
such a promise, it will get blocked until the computation is done and
an answer is available. If @scheme[force] is used with the promise as
it is forced from the same thread, an exception is raised.
In addition, these promises can be used with @scheme[sync], which
blocks until it has been forced. Note that using @scheme[sync] this
way is passive in the sense that it does not trigger evaluation of the
promise.}
@defform[(delay/thread body ...+)]{
@; TODO: document #:group keyword
This kind of promise begins the computation immediately, but this
happens on a separate thread. When the computation is done, the result
is cached as usual. Note that exceptions are caught as usual, and will
only be raised when @scheme[force]d. If such a promise is
@scheme[force]d before a value is ready, the calling thread will be
blocked until the computation terminates. These promises can also be
used with @scheme[sync].}
@defform[(delay/idle body ...+)]{
@; TODO: document #:wait-for, #:work-while, #:tick, #:use keywords
Similar to @scheme[delay/thread], but the computation thread gets to
work only when the process is otherwise idle, as determined by
@scheme[system-idle-evt], and the work is done in small runtime
fragements, making it overall not raise total CPU use or hurt
responsiveness. If the promise is @scheme[forced] before the
computation is done, it will run the rest of the computation immediately
without slicing the runtime. Using @scheme[sync] on these promises
blocks as is the case with @scheme[delay/sync], and this happens in a
passive way too, so the computation continues to work in low-priority.
@;{
TODO: Say something on:
* `use' = 0 --> similar to a plain `delay' which is evaluated only when
forced (or delay/sync, since it's still sync-able), except that the
evaluation is still happening on a new thread.
* `use' = 1 --> given cpu time as usual, but still polls the idle event
every `tick' seconds
* `use' = 1 and both `wait-for' and `work-while' are `always-evt' -->
similar to `delay/thread'.
* can use `wait-for' to delay evaluation start until some event is
ready. Specifically, this can be done to chain a few of these
promises sequentially.
* same goes for `work-while'. For example, you can use that with a
`semaphore-peek-evt' to be able to pause/resume the computation on
demand.
;}
}