Kent M Pitman <pitman@world.std.com> wrote:
+---------------
| Some Scheme implementations offer DYNAMIC-WIND, which is the generalization
| of special variable binding at the data level. They tend to stop short of
| providing syntax to make it as convenient as CL's specials, but it does
| offer more power.
+---------------
Several Scheme implementations also offer "fluid-let", which is much
more convenient to use than dynamic-wind if all you need is just
dynamic binding of variables. Fluid-let interacts correctly with
dynamic-wind (not surprising, since it's usually implemented using it).
MzScheme, in particular, also provides a mechanism called "parameters"
<URL:http://www.cs.rice.edu/CS/PLT/packages/doc/mzscheme/node98.htm>,
procedures that store some value inside themselves [for example,
"current-output-port"]; and a "parameterize" form, which does a
sort of fluid-let for parameters.
+---------------
| (DYNAMIC-WIND winder unwinder body)
|
| calls winder (a function of no arguments) to set up for the body, then
| calls unwinder (a function again of no arguments) to unset the body.
+---------------
Actually, following the minutes of the June '92 meeting, R5RS ordered
the arguments slightly differently:
(dynamic-wind before body after)
+---------------
| It's allowed to do this as many times as needed, not just at start and
| end of body, and can be used to implement the part of process
| scheduling that unwinds special variable bindings (as well as other
| dynamic bindings custom to the user's applications) on a process switch.
+---------------
The last time this topic came up, you also implied that dynamic-wind
was useful only for process switching, but it's far more general
than that. It's also useful wherever call/cc is used, e.g., doing
backtracking/nondeterminism, implementing "generators", for implementing
fluid-let, and as the basis of an exception system.
*Unfortunately* for the latter use, two things are missing from
the Scheme spec [though provided in most reasonable implementations]:
(1) There is no guarantee that primitive errors (e.g., divide-by-zero)
will use call/cc to throw the error, thus there's no guarantee that the
"after" thunk of dynamic-winds will be executed on errors; and (2) even
if #1 were fixed, R5RS says "The effect of using a captured continuation
to enter or exit the dynamic extent of a call to before or after is
undefined", which means that strictly speaking you can't use dynamic-wind
to implement "catch" or exception restarts. That is, the following code
isn't guaranteed to work:
(define (eval-safely expr)
(let ((done #f))
(call/cc
(lambda (k)
(dynamic-wind
(lambda () #f)
(lambda () (let ((val (eval expr)))
(set! done #t)
val))
(lambda () (when (not done) (k 'caught-error))))))))
Fortunately, in most Schemes, it does work: ;-}
> (eval-safely '(+ 1 2))
3
> (eval-safely '(/ 1 0))
/: division by zero
caught-error
>
-Rob
-----
Rob Warnock, 31-2-510 rpw3@sgi.com
Network Engineering http://reality.sgi.com/rpw3/
Silicon Graphics, Inc. Phone: 650-933-1673
1600 Amphitheatre Pkwy. PP-ASEL-IA
Mountain View, CA 94043