I still can't quite work out what question you are asking, but I think you want to know how you can modify bindings in a scope within functions which can't see the bindings. So here is an answer to that question.
The first thing to understand is that scope in modern programming languages is terribly simple: if you can see a binding (an association between a name and a value) then you have access to it, and if it's mutable you can mutate it. Pre-modern programming languages have all sorts of arcane rules which constrain this based on ease of implementation on tiny computers a long time ago (we are all cursed by the legacy of the PDP-11), but modern ones sweep all this away. Common Lisp is mostly a modern programming language in this sense.
So what you need to do is to capture a binding somehow, and then pass that captured binding into whatever functions you want to call, where it can be accessed or mutated. The way you capture a binding is with a function.
So, here is a simple example of how to do this in CL:
(defun foo (orig new)
(let ((x orig))
(bar (lambda (&optional (value nil valuep))
(if valuep
(setf x value)
x))
new)
x))
(defun bar (c new)
(format t "~&initially ~S~%" (funcall c))
(funcall c new)
(format t "~&then ~S~%" (funcall c)))
In this code the function created by the first argument to bar
has access to the binding of x
, and is so written so that calling it with no arguments will return the value of the binding, while calling it with an argument will set the value. Here is that in action:
CL-USER 5 > (foo 1 2)
initially 1
then 2
2
So you can see this works: the binding of x
is modified by calls to the function which captured it.
But this is syntactically clunky: it would be nice if we could avoid all these explicit funcall
s and lambda
s (we could avoid the former in a Lisp-1, but it's still not really nice). So here some code which does that (explanation of it below):
(defmacro capture (binding)
"Capture a binding"
(let ((value (make-symbol "VALUE"))
(valuep (make-symbol "VALUEP")))
`(lambda (&optional (,value nil ,valuep))
(if ,valuep
(setf ,binding ,value)
,binding))))
(defun captured (c &optional (value nil valuep))
"Return the value of a captured binding, or set it"
(if valuep
(funcall c value)
(funcall c)))
(defsetf captured captured)
OK, so the macro capture
is just syntactic sugar which creates a function identical to the one in the original code. It has to be a macro because the function needs to be created in the scope of the binding it is capturing.
captured
is then a trivial function which just calls the function created by capture
in a suitable way: so rather than saying (funcall c)
we can say (captured c)
.
Finally the defsetf
form teaches setf
how to set captured bindings, so (setf (captured x) y)
will work.
And here are reimplementations of the above foo
and bar
functions which use this:
(defun foo (orig new)
(let ((x orig))
(bar (capture x) new)
x))
(defun bar (c new)
(format t "~&initially ~S~%" (captured c))
(setf (captured c) new)
(format t "~&then ~S~%" (captured c)))
I think it's obvious that this is nicer to read than all the explicit funcall
s and lambda
s above. And it works the same way:
CL-USER 6 > (foo 1 2)
initially 1
then 2
2
Incidentally, you can capture expresssions, not just variable bindings, of course, so long as setf
knows what to do with them (so long as they are what it calls 'places'):
(defun fish (l)
(bone (capture (car l)))
l)
(defun bone (c)
(setf (captured c) 'bone))
And now
CL-USER 13 > (fish (list 1 2))
(bone 2)