add contributed scribbling for 'lazy'
svn: r8349
This commit is contained in:
parent
63c5d8a7cb
commit
739fde15cb
|
@ -14,6 +14,7 @@
|
|||
"Ian Barland, "
|
||||
"Eli Barzilay, "
|
||||
"Gann Bierner, "
|
||||
"Filipe Cabecinhas, "
|
||||
"Richard Cleis, "
|
||||
"John Clements, "
|
||||
"Richard Cobbe, "
|
||||
|
|
|
@ -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]
|
|
@ -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
226
collects/lazy/lazy.scrbl
Normal 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].
|
Loading…
Reference in New Issue
Block a user