Next: , Previous: Filenames, Up: System features


4.1.4 Fluid/dynamic bindings

The fluids structure provides a facility for dynamically bound resources, like special variables in Common Lisp, but with first-class, unforgeable objects.

Every thread in Scheme48 maintains a fluid or dynamic environment. It maps fluid descriptors to their values, much like a lexical environment maps names to their values. The dynamic environment is implemented by deep binding and dynamically scoped. Fluid variables are represented as first-class objects for which there is a top-level value and possibly a binding in the current dynamic environment. Escape procedures, as created with Scheme's call-with-current-continuation, also store & preserve the dynamic environment at the time of their continuation's capture and restore it when invoked.

The convention for naming variables that are bound to fluid objects is to add a prefix of $ (dollar sign); e.g., $foo.

— procedure: make-fluid top-level-value –> fluid

Fluid constructor.

— procedure: fluid fl –> value
— procedure: set-fluid! fl value –> unspecified
— procedure: fluid-cell-ref fluid-cell –> value
— procedure: fluid-cell-set! fluid-cell value –> unspecified

Fluid returns the value that the current dynamic environment associates with fl, if it has an association; if not, it returns fl's top-level value, as passed to make-fluid to create fl. Set-fluid! assigns the value of the association in the current dynamic environment for fl to value, or, if there is no such association, it assigns the top-level value of fl to value. Direct assignment of fluids is deprecated, however, and may be removed in a later release; instead, programmers should use fluids that are bound to mutable cells. Fluid-cell-ref and fluid-cell-set! are conveniences for this; they simply call the corresponding cell operations after fetching the cell that the fluid refers to by using fluid.

— procedure: let-fluid fluid value thunk –> values
— procedure: let-fluids fluid0 value0 fluid1 value1 ... thunk –> values

These dynamically bind their fluid arguments to the corresponding value arguments and apply thunk with the new dynamic environment, restoring the old one after thunk returns and returning the value it returns.

     (define $mumble (make-fluid 0))
     
     (let ((a (fluid $mumble))
           (b (let-fluid $mumble 1
                (lambda () (fluid $mumble))))
           (c (fluid $mumble))
           (d (let-fluid $mumble 2
                (lambda ()
                  (let-fluid $mumble 3
                    (lambda () (fluid $mumble)))))))
       (list a b c d))
         => (0 1 0 3)
     
     (let ((note (lambda (when)
                   (display when)
                   (display ": ")
                   (write (fluid $mumble))
                   (newline))))
       (note 'initial)
       (let-fluid $mumble 1 (lambda () (note 'let-fluid)))
       (note 'after-let-fluid)
       (let-fluid $mumble 1
         (lambda ()
           (note 'outer-let-fluid)
           (let-fluid $mumble 2 (lambda () (note 'inner-let-fluid)))))
       (note 'after-inner-let-fluid)
       ((call-with-current-continuation
          (lambda (k)
            (lambda ()
              (let-fluid $mumble 1
                (lambda ()
                  (note 'let-fluid-within-cont)
                  (let-fluid $mumble 2
                    (lambda () (note 'inner-let-fluid-within-cont)))
                  (k (lambda () (note 'let-fluid-thrown)))))))))
       (note 'after-throw))
         -| initial: 0
         -| let-fluid: 1
         -| after-let-fluid: 0
         -| outer-let-fluid: 1
         -| inner-let-fluid: 2
         -| let-fluid-within-cont: 1
         -| inner-let-fluid-within-cont: 2
         -| let-fluid-thrown: 0
         -| after-throw: 0