Lisp notes

Some notes on Lisp, specifically Common Lisp.

Special variables

Common Lisp's special variables are a wonderful thing. They allow you to do things which would be very hard to do without them. In particular, they allow you to maintain `dynamic state' - information about the state of some process which can be established and is then visible within the dynamic scope of its establishing form, and which will absolutely definitely be disestablished when the establishing form exits.

It's often thought that special variables can be simulated by an explicit shallow binding scheme. So long as there are global variables then the scheme would work something like this.

(let ((old *global-variable*))
  (unwind-protect
      (progn 
	(setf *global-variable* new)
	...code...)
    (setf *global-variable* old)))
    

So ...code... would see the dynamic `binding' of *global-variable*. This whole thing would then be hidden away in a macro called something like fluid-let

Of course, this can't work in the case of a multithreaded Lisp, because all the other threads that execute while the `binding' is in effect will also see the `binding', which is precisely what you don't want.

The fix often proposed for this problem is to add a special operator similar to Scheme's dynamic-wind operator. This is a generalised unwind-protect - it allows you to specify two sets of actions - one that happens when the stack is unwound for any reason - including a thread switch - and one that happens when it is rewound for any reason - including a thread switch. Scheme needs this because it has call/cc, which can cause just this kind of multiple exit/entry to happen. In fact the mechanism proposed for CL is not really like unwind-protect, because you would not generally want unwind-protect to do its thing on a thread switch.

The theory then is that, on a thread switch away from a thread, the stack would be `unwound', and the dynamic-wind form would catch this, and would restore the old value of the global. When a switch occurred back into the thread, the stack would be rewound, and the dynamic-wind form would reestablish the `bound' version of the global. The end result is to provide a mechanism which allows you to cool your own shallow-binding scheme. Further, once you have this mechanism you can use it for any kind of state, for instance you could dynamically bind functions. Eventually, you'd end up with a macro called letf which would be to let as setf is to setq.

This is a nice idea, but it's not a solution to the problem. It presupposes an implementation of multithreading where there are thread switches: in other words an implementation where multiple processors are emulated by one processor. On an implementation of multithreading where there really are multiple processors, there may be no thread switches - multiple threads can genuinely all run at once. So, on such an implementation this technique wont work, either for dynamic variables or any other kind of dynamic state. In general, if you want dynamic binding it has to be done at the level of the implementation, you can't fake it.

[TFEB]