Subject: Re: distinction?
From: rpw3@rigden.engr.sgi.com (Rob Warnock)
Date: 9 Dec 2000 14:06:25 GMT
Newsgroups: comp.lang.lisp
Message-ID: <90te91$8r0ao$1@fido.engr.sgi.com>
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