An Exercise in Extension

Chatting with a friend about dyanmically rebinding functions in Emacs, I wondered how I'd do the same in Common Lisp. Here's a quick code snack that serves up a solution.

What's the problem?

The basic problem to be solved today: function names defined in the global environment with defun are not special in the way that variable names defined with defvar are.

In code, you can do this:

     ;; define a variable  
     > (defvar *moo* "hey")  
     ;; define a function to print its value  
     > (defun print-moo ()  
         (print *moo*))  
     ;; then give it a new value  
     > (let ((*moo* "HEY HEY"))  
     "HEY HEY" 

But it doesn't work for functions:

     ;; define a function  
     > (defun goober (name)  
         (format nil "goober says: Hi ~a" name))  
       ;; define a function to call goober  
     > (defun call-goober (name)  
         (goober name))  
       ;; dynamic rebinding doesn't work.  
     > (flet ((goober (name)  
                (format nil "goober say: Hi ~a, this is custom!" name)))  
         (call-goober "colin"))  
       "goober says: Hi colin"  

A Sketched Solution

So, what you'd like to be able to do is something like:

> (dynamic-flet  
           (goober (name) (format nil "goober says: Hi ~a, this is custom!" name))  
         (call-goober "colin"))  
     "goober says: Hi colin, this is custom!" 

Getting there involves writing a macro to automatically set the symbol-function value or fdefinition value of a function name for the a limited extent, ensuring that the original value is reset when we're done.

Something like:

     ;; a sketch of a solution  
     (defmacro dynamic-flet ((name args &rest body) &body forms)  
       (let ((old-function (gensym)))  
         `(let ((,old-function (symbol-function ',name)))  
                   (setf (symbol-function ',name)  
                         (function (lambda ,args ,@body)))  
              (setf (symbol-function ',name) ,old-function))))) 

Which works by

  1. caching the original function in a temporary variable
  2. setting symbol-function for the given function to a new value
  3. evaluating some forms
  4. using unwind-protect to ensure that the old function value is restored, even in the presence of errors.

It might be wise to make the above definition more robust. For example, you could use fbound to check whether or not the provided name is actually bound to a function. Without such measures (symbol-function 'moocow) will signal an error when there is no function named moocow.

Lisp Rewards the Curious

It's neat that with the hypespec in one hand, and the REPL in the other, totally new constructs can be sculpted out of Lisp as "building material".

The above sketch of a dynamic-flet operator was not meant to be a complete implementation. I intended only to demonstrate how Common Lisp rewards curiosity in a rather unqique way. The combination of a 30 year old standard and a highly interactive environment pays dividends to anyone eager to invest the attention. I hope you'll find some time to play with, and improve upon, the above.