From 3b6e85aad0e494c2e4d81bb7f50a21627896fff1 Mon Sep 17 00:00:00 2001 From: Eli Barzilay Date: Sun, 15 Jul 2007 03:46:14 +0000 Subject: [PATCH] document the promise module svn: r6916 --- collects/lazy/doc.txt | 136 ++++++++++++++++++++++++++++++++------- collects/lazy/promise.ss | 8 +-- 2 files changed, 115 insertions(+), 29 deletions(-) diff --git a/collects/lazy/doc.txt b/collects/lazy/doc.txt index d85350bf5f..b098a69655 100644 --- a/collects/lazy/doc.txt +++ b/collects/lazy/doc.txt @@ -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] diff --git a/collects/lazy/promise.ss b/collects/lazy/promise.ss index a4c29a02bc..92108030a9 100644 --- a/collects/lazy/promise.ss +++ b/collects/lazy/promise.ss @@ -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 ) (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))))]))