The inits are evaluated in the current environment (in some unspecified order), the current values of the variables are saved, the results are assigned to the variables, the expressions are evaluated sequentially in the current environment, the variables are restored to their original values, and the value of the last expression is returned.
The syntax of this special form is similar to that of
fluid-lettemporarily rebinds existing variables. Unlike
fluid-letcreates no new bindings; instead it assigns the value of each init to the binding (determined by the rules of lexical scoping) of its corresponding variable.
An error of type
condition-type:unbound-variableis signalled if any of the variables are unbound. However, because
fluid-letoperates by means of side effects, it is valid for any variable to be unassigned when the form is entered. Here is an example showing the difference between
let. First see how
letaffects the binding of a variable:(define variable #t) (define (access-variable) variable) variable => #t (let ((variable #f)) (access-variable)) => #t variable => #t
#tin this case because it is defined in an environment with
fluid-let, on the other hand, temporarily reuses an existing variable:variable => #t (fluid-let ((variable #f)) ;reuses old binding (access-variable)) => #f variable => #t
The extent of a dynamic binding is defined to be the time period during which the variable contains the new value. Normally this time period begins when the body is entered and ends when it is exited; on a sequential machine it is normally a contiguous time period. However, because Scheme has first-class continuations, it is possible to leave the body and then reenter it, as many times as desired. In this situation, the extent becomes non-contiguous.
When the body is exited by invoking a continuation, the new value is saved, and the variable is set to the old value. Then, if the body is reentered by invoking a continuation, the old value is saved, and the variable is set to the new value. In addition, side effects to the variable that occur both inside and outside of body are preserved, even if continuations are used to jump in and out of body repeatedly.
Here is a complicated example that shows the interaction between dynamic binding and continuations:
(define (complicated-dynamic-binding) (let ((variable 1) (inside-continuation)) (write-line variable) (call-with-current-continuation (lambda (outside-continuation) (fluid-let ((variable 2)) (write-line variable) (set! variable 3) (call-with-current-continuation (lambda (k) (set! inside-continuation k) (outside-continuation #t))) (write-line variable) (set! inside-continuation #f)))) (write-line variable) (if inside-continuation (begin (set! variable 4) (inside-continuation #f)))))
Evaluating `(complicated-dynamic-binding)' writes the following on the console:
1 2 1 3 4
Commentary: the first two values written are the initial binding of
variable and its new binding after the
fluid-let's body is
entered. Immediately after they are written,
variable is set to
`3', and then
outside-continuation is invoked, causing us to
exit the body. At this point, `1' is written, demonstrating that
the original value of
variable has been restored, because we have
left the body. Then we set
variable to `4' and reenter the
body by invoking
inside-continuation. At this point, `3' is
written, indicating that the side effect that previously occurred within
the body has been preserved. Finally, we exit body normally, and write
`4', demonstrating that the side effect that occurred outside of
the body was also preserved.