diff --git a/collects/drscheme/acks.ss b/collects/drscheme/acks.ss index 2182f7d09d..b51bc73ab5 100644 --- a/collects/drscheme/acks.ss +++ b/collects/drscheme/acks.ss @@ -14,6 +14,7 @@ "Ian Barland, " "Eli Barzilay, " "Gann Bierner, " + "Filipe Cabecinhas, " "Richard Cleis, " "John Clements, " "Richard Cobbe, " diff --git a/collects/lazy/doc.txt b/collects/lazy/doc.txt deleted file mode 100644 index 915e1c2eee..0000000000 --- a/collects/lazy/doc.txt +++ /dev/null @@ -1,171 +0,0 @@ -_Lazy Scheme_ -------------- - -[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...) - -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 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 -forms that force strict behavior -- there are severals of these that -are useful in forcing different parts of a value in different ways: - -> (! expr) - evaluates `expr' strictly (the result is always forced (over and - over until it gets a non-promise value)). - -> (!! expr) - similar to `!', but recursively forces a structure (eg, lists). - -> (!!! expr) - similar to `!!', but also wraps procedures that it finds so their - outputs are forced (so they are usable in a astrict world). - -> (!list expr) - forces the `expr' which is expected to be a list, and forces the - cdrs recursively to expose a proper list structure. - -> (!!list expr) - similar to `!list', but also forces (using `!') the elements of the - list. - -There are a few side-effect bindings that are provided as is. For -example, `read' and `printf' do the obvious thing -- but note that the -language is a call-by-need, and you need to be aware when promises are -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 - -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 - is used to split such a tuple to actual multiple values. (This may - change in the future.) - -> (!values expr) - forces `expr', and uses `split-values' on the result. - -> (!!values expr) - similar to `!values', but forces each of the values (not - recursively). - - -_mz-without-promises.ss_ ------------------------- - -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". - - -_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. - -Note: `lazy' cannot be used with an expression that evaluates to -multiple values. `delay' is, however, is fine with multiple values. -(This is for efficiency in the lazy language, where multiple values -are avoided.) - -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/info.ss b/collects/lazy/info.ss index 093851885c..d3e56d28f7 100644 --- a/collects/lazy/info.ss +++ b/collects/lazy/info.ss @@ -1,7 +1,7 @@ (module info setup/infotab (require (lib "string-constant.ss" "string-constants")) (define name "Lazy Scheme") - (define doc.txt "doc.txt") + (define scribblings '(("lazy.scrbl"))) (define drscheme-language-modules '(("lazy.ss" "lazy"))) (define drscheme-language-positions `((,(string-constant experimental-languages) "Lazy Scheme"))) diff --git a/collects/lazy/lazy.scrbl b/collects/lazy/lazy.scrbl new file mode 100644 index 0000000000..d93bdf4f52 --- /dev/null +++ b/collects/lazy/lazy.scrbl @@ -0,0 +1,226 @@ +#lang scribble/doc +@(require (for-label scheme/base + scheme/contract + (only-in lazy/force + ! !! !!! + !list !!list + split-values !values !!values) + (only-in lazy/promise + delay force lazy promise?))) + +@(begin + (define-syntax-rule (def-scheme scheme-force scheme-delay scheme-promise?) + (begin + (require (for-label scheme/promise)) + (define scheme-force (scheme force)) + (define scheme-delay (scheme delay)) + (define scheme-promise? (scheme promise?)))) + (def-scheme scheme-force scheme-delay scheme-promise?)) + +@; ---------------------------------------- + +@(require scribble/manual) + +@title{@bold{Lazy Scheme}} + +@defmodulelang[lazy] + +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 +@schememodname[lazy] as your module's language: + +@schememod[ +lazy +... lazy code here...] + +Function applications are delayed, and promises are automatically +forced. The language provides bindings that are equivalent to most of +the @schememodname[mzscheme] and @schememodname[scheme/list] +libraries. Primitives are strict in the expected places; struct +constructors are lazy; @scheme[if], @scheme[and], @scheme[or] @|etc| +are plain (lazy) functions. Strict functionality is provided as-is: +@scheme[begin], I/O, mutation, parameterization, etc. To have your +code make sense, you should chain side effects in @scheme[begin]s, +which will sequence things properly. (Note: This is similar to +threading monads through your code---only use @scheme[begin] where +order matters.) + +Mizing lazy and strict code is simple: you just write the lazy code in +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. + +A few side-effect bindings are provided as-is. For example, +@scheme[read] and @scheme[printf] do the obvious thing---but note that +the language is a call-by-need, and you need to be aware when promises +are forced. There are also bindings for @scheme[begin] (delays a +computation that forces all sub-expressions), @scheme[when], +@scheme[unless], etc. There are, however, less reliable and might +change (or be dropped) in the future. + +There are a few additional bindings, the important ones are special +forms that force strict behaviour---there are several of these that +are useful in forcing different parts of a value in different ways. + +@; ---------------------------------------- + +@section{Forcing Values} + +@defmodule[lazy/force] + +The bindings of @schememodname[lazy/force] are re-provided by +@schememodname[lazy]. + +@defproc[(! [expr any/c]) any/c]{ + +Evaluates @scheme[expr] strictly. The result is always forced, over +and over until it gets a non-promise value.} + + +@defproc[(!![expr any/c]) any/c]{ + +Similar to @scheme[!], but recursively forces a structure (e.g: +lists).} + + +@defproc[(!!! [expr any/c]) any/c]{ + +Similar to @scheme[!!], but also wraps procedures that if finds so +their outputs are forced (so they are useful in a strict world).} + + +@defproc[(!list [expr (or/c promise? list?)]) list?]{ + +Forces the @scheme[expr] which is expected to be a list, and forces +the @scheme[cdr]s recursively to expose a proper list structure.} + + +@defproc[(!!list [expr (or/c promise? list?)]) list?]{ + +Similar to @scheme[!list] but also forces (using @scheme[!]) the +elements of the list.} + + +@subsection{Multiple values} + +To avoid dealing with multiple values, they are treated as a single +tuple in the lazy language. This is implemented as a +@scheme[multiple-values] struct, with a @scheme[values] slot. + + +@defproc[(split-values [x multiple-values?]) any]{ + +Used to split such a tuple to actual multiple values. (This may change +in the future.)} + + +@defproc[(!values [expr (or/c promise? multiple-values?)]) any]{ + +Forces @scheme[expr] and uses @scheme[split-values] on the result.} + + +@defproc[(!!values [expr (or/c promise? multiple-values?)]) any]{ + +Similar to @scheme[!values], but forces each of the values +recursively.} + +@; ---------------------------------------- + +@section{Promises} + +@defmodule[lazy/promise] + +The @schememodname[lazy/promise] module implements lazy promises as +implicitly used by the @schememodname[lazy] language. + +Note: this module implements a new kind of promises. MzScheme's +promises are therefore treated as other values---which means that they +are not forced by this module's @scheme[force]. + +Generally speaking, if you use only @scheme[delay], @scheme[force], +and @scheme[promise?], you get the same functionality as in Scheme. +See below for two (relatively minor) differences. + +@scheme[lazy] implements a new kind of promise. When used with +expressions, it behaves like @scheme[delay]. However, when +@scheme[lazy] is used with an expression that already evaluates to a +promise, it combines with it such that @scheme[force] will go through +both promises. In other words, @scheme[(lazy _expr)] is equivalent to +@scheme[(lazy (lazy _expr))]. The main feature of this implementation +of promises is that @scheme[lazy] is safe-for-space (see +@link["http://srfi.schemers.org/srfi-45/"]{SRFI-45} for +details)---this is crucial for tail-recursion in Lazy Scheme. + +To summarize, a sequence of @scheme[lazy]s is forced with a single use +of @scheme[force], and each additional @scheme[delay] requires an +additional @scheme[force]---for example, @scheme[(lazy (delay (lazy +(delay (lazy _expr)))))] requires three @scheme[force]s to evaluate +@scheme[_expr]. + +Note: @scheme[lazy] cannot be used with an expression that evaluates +to multiple values. @scheme[delay], however, is fine with multiple +values. (This is for efficiency in the lazy language, where multiple +values are avoided.) + +As mentioned above, using @scheme[delay] and @scheme[force] is as in +Scheme, except for two differences. The first is a +technicality---@scheme[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 value and a promise. + +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): + +@schemeblock[ +(let ([count 5]) + (define p + (delay (if (<= count 0) + count + (begin (set! count (- count 1)) + (force p))))) + (force p))] + +returns 0 with Scheme's @|scheme-delay|/@|scheme-force|, but aborts +with an error with this module's promises. This restriction leads to +faster code (see +@link["http://srfi.schemers.org/srfi-45/post-mail-archive/msg00011.html"]{a +SRFI-45 discussion post} for some additional details), while +preventing diverging code (the only reasonable way to use circular +promises is using mutation as above). + + +@defform[(delay expr)]{Similar in functionality to Scheme's @|scheme-delay|} + + +@defform[(lazy expr)]{Creates a ``lazy'' promise. See above for +details.} + + +@defproc[(force [x any/c]) any/c]{ + +Forces a promise that was generated by @scheme[delay] or +@scheme[lazy]. Similar to Scheme's @|scheme-force|, except that +non-promise values are simply returned.} + + +@defproc[(promise? [x any/c]) boolean?]{ + +A predicate for promise values.} + +@; ---------------------------------------- + +@section{MzScheme without Promises} + +@defmodule[lazy/mz-without-promises] + +The @schememodname[lazy/mz-without-promises] module simply provides +all of @schememodname[mzscheme] except for promise-related +functionality: @|scheme-delay|, @|scheme-force|, and +@|scheme-promise?|. This is because @schememodname[lazy/promise] +defines and provides the same names. It is intended as a helper, but +you can use it together with @schememodname[lazy/promise] to get a +@schememodname[mzscheme]-like language where promises are implemented +by @schememodname[lazy/promise].