add contributed scribbling for 'lazy'
svn: r8349
This commit is contained in:
parent
63c5d8a7cb
commit
739fde15cb
|
@ -14,6 +14,7 @@
|
||||||
"Ian Barland, "
|
"Ian Barland, "
|
||||||
"Eli Barzilay, "
|
"Eli Barzilay, "
|
||||||
"Gann Bierner, "
|
"Gann Bierner, "
|
||||||
|
"Filipe Cabecinhas, "
|
||||||
"Richard Cleis, "
|
"Richard Cleis, "
|
||||||
"John Clements, "
|
"John Clements, "
|
||||||
"Richard Cobbe, "
|
"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
|
(module info setup/infotab
|
||||||
(require (lib "string-constant.ss" "string-constants"))
|
(require (lib "string-constant.ss" "string-constants"))
|
||||||
(define name "Lazy Scheme")
|
(define name "Lazy Scheme")
|
||||||
(define doc.txt "doc.txt")
|
(define scribblings '(("lazy.scrbl")))
|
||||||
(define drscheme-language-modules '(("lazy.ss" "lazy")))
|
(define drscheme-language-modules '(("lazy.ss" "lazy")))
|
||||||
(define drscheme-language-positions
|
(define drscheme-language-positions
|
||||||
`((,(string-constant experimental-languages) "Lazy Scheme")))
|
`((,(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