Monthly Archives: June 2014

The less-familiar parts of Lisp for beginners — restart-case

Having just talked about restart-bind, we’ll now look at restart-case.  The restart-bind might be used to implement restart-case, but there are differences between the syntax and behaviour for these two macros.  The most obvious difference from the programmer’s perspective is in what happens after a restart is invoked.  In the case of restart-bind, the execution continues after the invoke-restart call.  In the case of restart-case, the entire restart-case form exits, and the values returned by the restart, if any, are returned.  Here is a simple example of the two different behaviours:
*slime-repl sbcl*

CL-USER> (restart-bind
             ((say-hi #'(lambda ()
                          (format t "Hello~%")
                          (values 1 2 3))))
           (progn
             (format t "Printing before the restart~%")
             (invoke-restart 'say-hi)
             (format t "Printing after the restart~%")
             (format t "Returning the value 10~%")
             10))
Printing before the restart
Hello
Printing after the restart
Returning the value 10
10
CL-USER> (restart-case
             (progn
               (format t "Printing before the restart~%")
               (invoke-restart 'say-hi)
               (format t "Printing after the restart~%")
               (format t "Returning the value 10~%")
               10)
           (say-hi ()
             (format t "Hello~%")
             (values 1 2 3)))

Printing before the restart
Hello
1
2
3

The less-familiar parts of Lisp for beginners — restart-bind

Continuing through the list of somewhat obscure Lisp features, we come to restart-bind.  This is a fairly low-level tool for configuring restarts.  A review of Lisp restarts is available in this earlier article.  While the restart system can appear complicated, it is generally useful to think of restarts as dynamically-scoped functions that can be invoked under certain conditions.  The restart-bind macro is the low-level glue to handle this, and it’s unusual that the full power of this macro is needed.  If you can achieve your intended results with restart-case, it is recommended that you use that, instead.

The restart-bind macro has three optional key/value pair arguments that affect behaviour in interactive contexts.

A simple example of the basic syntax follows.  Note that this example could also be easily coded with restart-case:
*slime-repl sbcl*

CL-USER> (restart-bind
             ((say-hi #'(lambda ()
                          (format t "Hello~%"))))
           (dotimes (i 10)
             (format t "i= ~D~%" i)
             (when (= (mod i 4) 3)
               (invoke-restart 'say-hi))))
i= 0
i= 1
i= 2
i= 3
Hello
i= 4
i= 5
i= 6
i= 7
Hello
i= 8
i= 9
NIL

The less-familiar parts of Lisp for beginners — remprop

Now, we’ll discuss remprop a bit.  You may recall an earlier article where I discussed the concept of Lisp symbols.  There, I talked about symbols having five fields, such as the function field or the value field.  I mentioned the “property list” field, but didn’t say anything more about it.  Now, the time has come to examine that.

Now, while a symbol can have an associated value field, it can also have an associated property list that allows it to hold a set of key/value pairs.  A property list is a list with an even number of entries, in which each successive pair of entries represents a key and associated value.  Conceptually, but not structurally, it resembles an association list.  The remprop function allows the user to remove the first pair whose key is eq to the value passed in the function’s second argument.  Note that there is no way to modify the test function, and the use of eq rather than equal means that the programmer should avoid assigning to the property list with keys that are numbers or characters.

Now, I haven’t found reasons to use property lists so far.  In other projects, they have been used to attach meaningful data to functions.  For instance, one might attach a callback function to a symbol associated with a particular function, rather than building a struct that contains the function and its associated callback.

One warning: do not think of property lists as another way to build slots in a structure, the property lists are bound to a symbol, not to an instance, and copying the value of a symbol does not copy its property list.

The less-familiar parts of Lisp for beginners — remove-method

Now we come to another Lisp feature with no real analogue in C++, the standard generic function remove-method.  What this function does is to undo the effect of a defmethod construct, at run time.  That is, the Lisp image can forget about a specialized method.  As we’ve noted several times in the past, this difference comes about because of a fundamental difference in the way Lisp and C++ runtime environments are handled.  In C++, you modify the source code, recompile the binary, and then restart the program.  In Lisp, the image itself contains the methods, functions, and data that any Lisp program might want to use, but it’s better not to think of the program as really existing, it’s more like a context that happens to be executing functions at the moment.

In fact, now that I think of it, a C++ programmer might better understand a Lisp “program” as a particular thread executing in a larger context.  The thread has an associated stack and local data, and runs through certain functions, and maybe eventually exits, but does all this in the context of a sea of functions and methods available to it.  Different threads can run entirely different “programs”, and as long as nobody starts messing around with global state, these programs don’t even know about one another.  So, think of your C++ program was really just a launching pad for these threads, and you have the ability to modify methods and classes on the fly then run a “program” thread using the updated binary image.  That’s a bit like how you can view the Lisp runtime environment.

So, remove-method removes a specialized method, as if the corresponding defmethod had never been loaded.  Here’s an example of this in use.  I create a base class and a derived class, each with different methods for do-something.  Then, I make an instance of the derived class, and I locate the method specialized on the derived class.  The first time I call do-something, it uses the method for the derived class.  Then, I call remove-method on that method, and call do-something again.  Now, with no specialized method for the derived class, the base-class method is called instead:
remove-method.lisp

(defclass base-class ()
  ())

(defclass derived-class (base-class)
  ())

(defgeneric do-something (obj))

(defmethod do-something ((obj base-class))
  (format t "Called do-something in the base class.~%"))

(defmethod do-something ((obj derived-class))
  (format t "Called do-something in the derived class.~%"))

(defun demonstrate ()
  (let ((obj (make-instance 'derived-class))
        (specialized 
         (find-method #'do-something
                      '()
                      (list (find-class 'derived-class)))))
    (do-something obj)
    (remove-method #'do-something specialized)
    (do-something obj)))

With output:
*slime-repl sbcl*
CL-USER> (demonstrate)
Called do-something in the derived class.
Called do-something in the base class.
NIL