Go to the previous, next section.
special form+: fluid-let ((variable init) ...) expression expression ...
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 let
, but
fluid-let
temporarily rebinds existing variables. Unlike
let
, fluid-let
creates 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.
MIT Scheme allows any of the inits to be omitted, in which case the corresponding variables are temporarily unassigned.
An error of type condition-type:unbound-variable
is signalled if
any of the variables are unbound. However, because
fluid-let
operates 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 fluid-let
and
let
. First see how let
affects the binding of a variable:
(define variable #t) (define (access-variable) variable) variable => #t (let ((variable #f)) (access-variable)) => #t variable => #t
access-variable
returns #t
in this case because it
is defined in an environment with variable
bound to
#t
. 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.
Go to the previous, next section.