Next: , Previous: Lexical Binding, Up: Special Forms


2.3 Dynamic Binding

— 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/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, 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.