document the promise module

svn: r6916
This commit is contained in:
Eli Barzilay 2007-07-15 03:46:14 +00:00
parent 1119702903
commit 3b6e85aad0
2 changed files with 115 additions and 29 deletions

View File

@ -1,23 +1,32 @@
This collection implements _Lazy Scheme_.
_Lazy Scheme_
-------------
It is available as a language level and as a module that can be used
to write lazy code. Code that uses the "lazy.ss" as a module
language:
[This documentation begins with a description of the lazy language,
and later on provides a description of all modules. Specifically, the
"promise.ss" module (together with the "force.ss" module) can be used
as a better alternative to MzScheme's promises.]
Lazy Scheme is available as both a language level and a module that
can be used to write lazy code. To write lazy code, simply use
"lazy.ss" as your module's language:
(module foo (lib "lazy.ss" "lazy")
...)
...lazy code here...)
is lazy code, which is implemented using standard promises: function
applications are delayed, and promises are automatically forced. The
language provides bindings that are equivalent to most of MzScheme and
the list library. Primitives are strict in the expected places;
struct constructors are lazy; `if', `and', `or' etc are plain (lazy)
functions.
Function applications are delayed, and promises are automatically
forced. The language provides bindings that are equivalent to most of
MzScheme and the list library. Primitives are strict in the expected
places; struct constructors are lazy; `if', `and', `or' etc are plain
(lazy) functions. Strict functionality is provided as is: begin, I/O,
mutation, parameterization, etc. To have your code make sense, you
should chain side effects in `begin's which will sequence things
properly. (Note: this is similar to threading monads through your
code -- only use `begin' where order matters.)
Mixing lazy and strict code is simple: you just write the lazy code in
the lazy language, and the rest as usual. The lazy language treats
imported functions (that were not defined in the lazy language) as
strict, and on the strict side you only need to force (possibly
the lazy language, and strict code as usual. The lazy language treats
imported functions (those that were not defined in the lazy language)
as strict, and on the strict side you only need to force (possibly
recursively) through promises.
There are a few additional bindings, the important ones are special
@ -50,12 +59,10 @@ forced. There are also bindings for `begin' (delays a computation
that forces all sub-expressions), `when', `unless', etc. These are,
however, less reliable and might change (or be dropped) in the future.
* Multiple values
Multiple values
---------------
Also, to avoid dealing with multiple values, they are treated as a
single tuple in the lazy language. This is implemented as a
To avoid dealing with multiple values, they are treated as a single
tuple in the lazy language. This is implemented as a
`multiple-values' struct, with a `values' slot.
> split-values
@ -70,11 +77,90 @@ single tuple in the lazy language. This is implemented as a
recursively).
Making strict code interact with lazy code
------------------------------------------
_mz-without-promises.ss_
------------------------
To make it easy for strict code to interact with lazy code, use the
_force.ss_ module: it provides the above bindings (as functions) that
can be used to force promises in various ways.
This is a tiny module that provides all of `mzscheme', except for
promise-related functionality (`delay', `force', `promise?'). This is
because "promise.ss" defines and provides the same names. It is
intended as a helper, but you can use it together with "promise.ss" to
get a mzscheme-like language where promises are implemented by
"promise.ss".
[More documentation will be added.]
_promise.ss_
------------
This module implements lazy promises. It provides the following four
bindings:
> (delay expr) [syntax]
Similar in functionality to Scheme's `delay'.
> (lazy expr) [syntax]
Creates a `lazy' promise. See below for details.
> (force x) [procedure]
Force a promise that was generated by `delay' or `lazy'. Similar to
Scheme's `force', except that non-promise values are simply returned.
> (promise? x) [procedure]
A predicate for promise values.
Note: this module implements a *new* kind of promises. MzScheme
promises are therefore treated as other values -- which means that
they are not forced by this module's `force'.
Generally speaking, if you use only `delay', `force', and `promise?',
you get the same functionality as in Scheme. See below for two
(relatively minor) differences.
`lazy' implements a new kind of promise. When used with expressions,
it behaves like `delay'. However, when `lazy' is used with an
expression that already evaluates to a promise, it combines with it
such that `force' will go through both promises. In other words,
`(lazy X)' is equivalent to `(lazy (lazy X))'. The main feature of
this implementation of promises is that `lazy' is safe-for-space (see
srfi-45 for details) -- this is crucial for tail-recursion in Lazy
Scheme.
To summarize, a sequence of `lazy's is forced with a single use of
`force', and each additional `delay' requires an additional `force' --
for example,
(lazy^i (delay (lazy^j (delay (lazy^k E)))))
requires three `force's to evaluate E.
As mentioned above, using `delay' and `force' is as in Scheme, except
for two differences. The first is a technicality -- force is an
identity for non-promise values. This makes it more convenient in
implementing the lazy language, where there is no difference between a
values and a promises.
The second difference is that circular (re-entrant) promises are not
permitted (i.e., when a promise is being forced, trying to force it in
the process will raise an error). For example, the following code
(see srfi-45 for additional examples):
(let ([count 5])
(define p (delay (if (<= count 0)
count
(begin (set! count (- count 1)) (force p)))))
(force p))
returns 0 with Scheme's `delay'/`force', but aborts with an error with
this module's promises. This restriction leads to faster code (see
http://srfi.schemers.org/srfi-45/post-mail-archive/msg00011.html for
some additional details), while preventing diverging code (the only
reasonable way to use circular promises is using mutation as above).
_force.ss_
----------
[TODO]

View File

@ -14,7 +14,7 @@
;; (define-struct promise (p)) <-- use a more sophisticated struct below
;; promise that can print in meaningful ways
;; Promise records (note: print in meaningful ways like thunks)
(define-values (promise promise? p:ref p:set!)
(let*-values
([(printer)
@ -44,7 +44,7 @@
;; | (promise <promise>) (shared promise)
;; | (promise #f) (currently running)
;; creates a `composable' promise
;; Creates a `composable' promise
;; X = (force (lazy X)) = (force (lazy (lazy X))) = (force (lazy^n X))
(define-syntax (lazy stx)
(syntax-case stx ()
@ -53,14 +53,14 @@
'inferred-name (syntax-local-name))])
(syntax/loc stx (promise proc)))]))
;; creates a promise that does not compose
;; Creates a promise that does not compose
;; X = (force (delay X)) = (force (lazy (delay X)))
;; = (force (lazy^n (delay X)))
;; X = (force (force (delay (delay X)))) =/= (force (delay (delay X)))
;; so each sequence of `(lazy^n o delay)^m' requires m `force's and a
;; sequence of `(lazy^n o delay)^m o lazy^k' requires m+1 `force's (for k>0)
;; (This is not needed with a lazy language (see the above URL for details),
;; but provided for completeness)
;; but provided for completeness.)
(define-syntax (delay stx)
(syntax-case stx ()
[(delay expr) (syntax/loc stx (lazy (promise (list expr))))]))