I would like to argue against any DYNAMIC-WIND + SET! sort of
"fluid variable" system. The problem is threads. If you have a
thread model, then any thread switch involves unwinding up the
stack to the common ancestor continuation, then winding down into
the activated continuation. This seems unacceptably expensive; thread
switch should be a low-overhead operation.
Because of this issue, I strongly prefer making fluid variables
a primitive construct. Scheme48's system is a pretty canonical example
of this genre. "Fluid variables" are data structures. So you have the
following primitive procedures:
(MAKE-FLUID value) -> fluid
(LET-FLUID fluid value thunk)
(LET-FLUIDS fluid1 value1 ... fluidn valuen thunk)
(FLUID fluid) -> value
(SET-FLUID! fluid value)
There are no primitive syntax forms. This design is not unique to S48;
I believe it was proposed by someone other than Kelsey & Rees, and
is used elsewhere (but I can't recall who or where).
Fluid values are cells that have dynamic scope, as provided by LET-FLUID.
You typically bind them to global vars, e.g.
(define $cwd (make-fluid #f))
(define (with-cwd dir thunk) (let-fluid $cwd dir thunk))
Throwing in or out of LET-FLUID scope does the right thing, as you'd want.
Single-threaded implementations can provide fluids using DYNAMIC-WIND,
*but* multithreaded implementatins can implement fluids using deep
binding techniques, providing fast thread switch. This is not possible
with a system that actually effects variables.
If we're going to propose a dynamic-binding system, we should be careful
to do one that will work in both multi-threaded as well as single-threaded
systems -- that's planning ahead.
-Olin