From: Gilbert Baumann
Subject: Re: How to do this in Lisp?
Date: 
Message-ID: <GILBERT.96Oct12123837@ma2s2.mathematik.uni-karlsruhe.de>
In article <··········@Godzilla.cs.nwu.edu> ·····@cs.nwu.edu (Seth Tisue) writes:
   In article <·················@netcom.com>,
   Bill Newman <·······@netcom.com> wrote:
   >I received e-mail suggesting -- if I understood correctly -- that I
   >can't overload assignment in Lisp, but the same effect could be
   >achieved by defining my own (REVERSIBLE-ASSIGN VAR VALUE) function or
   >macro in Lisp.  I can't figure out how to do even this.  Also, even if I
   >could, I'd still prefer to have the reversible-ness associated with
   >each variable, rather than with each assignment to each variable.  (I
   >used to do it the other way around in C before I switched to C++, and
   >it was a major maintenance hassle tracking down things which
   >accidentally changed something irreversibly.)

   I realized after sending my last message that actually there is
   already a way of doing this built into Lisp -- it's not exactly the
   same thing, but it might be just what you need.  Suppose you're
   storing your board in the global variable *board*.  If you declare
   *board* to be "special" (i.e., to have dynamic scope), then saying:

     (let ((*board* <new-value>))
       ...)

   does exactly what you want.  *board* takes on a new value for the
   duration of the dynamic extent of the LET, and automatically returns
   to its old value when the LET exits.  Dynamic scope means that *board*
   has the new value even in the bodies of functions invokved in the body
   of the LET.

You can also easily define your on version of LET, which binds
arbitrary SETFable fields and undoes the bindings when leaving the
body. LETF would be the obvious way to call it, but I have the strange
feeling, that there is a LETF allready somewhere in some Lisp [At
least somebody could be confused, since there is a FLET in CL], so I
call it better BINDF:

(defmacro bindf (bindings &body body)
  (let ((vars (mapcar #'(lambda (x) (declare (ignore x)) (gensym))
                      bindings)))
    `(LET ,(mapcar #'(lambda (var bind) `(,var ,(car bind)))
                   vars bindings)
       (UNWIND-PROTECT
           (PROGN
             (PSETF ,@(mapcan #'(lambda (bind) (list (car bind) (cadr bind)))
                              bindings))
             ,@body)
         (SETF ,@(mapcan #'(lambda (var bind) (list (car bind) var))
                         vars bindings)) )) ))

However this macro is not complete, things got evaluated multiple, you
will have to insert the usual klugdes here to prevent this. Now it
works like this:

> (setf x '#(a b c))
#(A B C)
> (bindf (((aref x 2) 'quux)
          ((aref x 1) 'fred))
     (print x))

#(A FRED QUUX)      ;This is what print barks
#(A B C)            ;This is the return value of print, hence x.
                    ; Note that the 'binding' of (aref x 2) and
                    ; (aref x 1) are undone here.
> x                 ;Just to verify that
#(A B C)            ;o.k. It seems to work.
> (macroexpand '(bindf (((aref x 2) 'quux)           ;lets see what 
                        ((aref x 1) 'fred))          ; it does
                    (print x)))

(LET ((#:G788 (AREF X 2))
      (#:G789 (AREF X 1)))
     (UNWIND-PROTECT
         (PROGN (PSETF (AREF X 2) 'QUUX
                       (AREF X 1) 'FRED)
                (PRINT X))
       (SETF (AREF X 2) #:G788
             (AREF X 1) #:G789) ))
T
> 

Looks cute, doesn't it?

I imagine, having some board and want to make a move by putting a
stone from positions pos-1 to pos-2. As less as I know about go, this
does not happen with go, but in checkers, it does apply [I had
implemented a little checkers playing game some month ago.], you would
say:

(bindf (((aref board pos-2) (aref board pos-1))
        ((aref board pos-1) 'empty))
   .. do something ..
   .. heavy calculation ..
   )

Now (aref board pos-1) and (aref board pos-2) would have their
old values.

Hope somebody could make use of it,
                               Gilbert.