add contributed scribbling for 'lazy'

svn: r8349
This commit is contained in:
Matthew Flatt 2008-01-16 14:39:56 +00:00
parent 63c5d8a7cb
commit 739fde15cb
4 changed files with 228 additions and 172 deletions

View File

@ -14,6 +14,7 @@
"Ian Barland, "
"Eli Barzilay, "
"Gann Bierner, "
"Filipe Cabecinhas, "
"Richard Cleis, "
"John Clements, "
"Richard Cobbe, "

View File

@ -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]

View File

@ -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")))

226
collects/lazy/lazy.scrbl Normal file
View File

@ -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].