Prev Up Next

Here is a definition of a rather more complicated macro, fluid-let (sec 5.2). fluid-let specifies temporary bindings for a set of already existing lexical variables. Given a fluid-let expression such as

(fluid-let ((x 9) (y (+ y 1)))
  (+ x y))

we want the expansion to be

(let ((OLD-X x) (OLD-Y y))
  (set! x 9)
  (set! y (+ y 1))
  (let ((RESULT (begin (+ x y))))
    (set! x OLD-X)
    (set! y OLD-Y)
    RESULT))

where we want the identifiers OLD-X, OLD-Y, and RESULT to be symbols that will not capture variables in the expressions in the fluid-let form.

Here is how we go about fashioning a fluid-let macro that implements what we want:

(define-macro fluid-let
  (lambda (xexe . body)
    (let ((xx (map car xexe))
          (ee (map cadr xexe))
          (old-xx (map (lambda (ig) (gensym)) xexe))
          (result (gensym)))
      `(let ,(map (lambda (old-x x) `(,old-x ,x)) 
                  old-xx xx)
         ,@(map (lambda (x e)
                  `(set! ,x ,e)) 
                xx ee)
         (let ((,result (begin ,@body)))
           ,@(map (lambda (x old-x)
                    `(set! ,x ,old-x)) 
                  xx old-xx)
           ,result)))))

The macro's arguments are: xexe, the list of variable/expression pairs introduced by the fluid-let; and body, the list of expressions in the body of the fluid-let. In our example, these are ((x 9) (y (+ y 1))) and ((+ x y)) respectively.

The macro body introduces a bunch of local variables: xx is the list of the variables extracted from the variable/expression pairs. ee is the corresponding list of expressions. old-xx is a list of fresh identifiers, one for each variable in xx. These are used to store the incoming values of the xx, so we can revert the xx back to them once the fluid-let body has been evaluated. result is another fresh identifier, used to store the value of the fluid-let body. In our example, xx is (x y) and ee is (9 (+ y 1)). Depending on how your system implements gensym, old-xx might be the list (GEN-63 GEN-64), and result might be GEN-65.

The output list is created by the macro for our given example looks like

(let ((GEN-63 x) (GEN-64 y))
  (set! x 9)
  (set! y (+ y 1))
  (let ((GEN-65 (begin (+ x y))))
    (set! x GEN-63)
    (set! y GEN-64)
    GEN-65))

which matches our requirement.

Prev Up Next

Log in or register to write something here or to contact authors.