Next: Definitions, Previous: Lexical Binding, Up: Special Forms
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
, butfluid-let
temporarily rebinds existing variables. Unlikelet
,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/GNU 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, becausefluid-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 betweenfluid-let
andlet
. First see howlet
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 withvariable
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.