setup-swank: an example of a self-modifying function
This post came from a short-lived blog I tried to run (tail-recursion.net) where I wanted to write up a short bit of code I'd written that week.
This function sets up a toggleable swank listener in a Lisp program. This enables a REPL directly in the running process, providing unparalled introspection, debugging, and speed of development. It performs some initial setup, sets up a closure over a status variable, and then swaps out the setup code for the production code.
(defun start-swank (&key (port 4006))
"Setup a swank server in the running process."
The swank-running-p variable is used to keep track of the function's
state: is swank running?
(let ((swank-runningp nil))
We create a new, locally-scoped function.
(labels ((fn ()
(progn
(if swank-runningp
(progn
If swank is running, we print a message to the console alerting the programmer that swank is shutting down.
(format t "Stopping swank server...~%")
Then, stop swank.
(swank:stop-server port)
Finally, update the swank status variable.
(setf swank-runningp nil))
(progn
If swank is not running, let the programmer know that it is being started.
(format t "Starting swank server on port ~A.~%" port)
Start the server, and make sure that it doesn't close when the function exits.
(swank:create-server :port port :dont-close t)
Finally, update our status.
(setf swank-runningp t)))
After taking the appropriate action, we'll return the current swank state so that it can be programmatically acted on.
swank-runningp)))
Swap out the setup code for our locally defined function.
(setf (symbol-function 'start-swank) #'fn)
The function is called start-swank, so we start swank.
(start-swank))))
The untransformed code is available.