Tuesday, February 19, 2008

Continuing and restarts

It's been a while since I have posted anything, mostly due to the fact that I'm teaching an undergraduate course on programming languages. Since today is a new holiday (ok, yesterday, technically) in my province ("Family Day"), I actually have a little free time. That hasn't happened in a while.

Lately, I've been working on an approach to dealing with certain kinds of errors such that evaluation continues, even though it may not be entirely correct. This has me working with one of my favourite features of Lisp or any other language: restarts. Restarts are not only a wonder to behold, but provide a lot of potential. The primary reason, in my opinion, is that you can continue computation in the face of some problem that doesn't have an immediately obvious solution.

That being said, the usefulness of restarts is limited by those who provide them. In your own code, you can add restarts as you see fit, but if you want to try and recover from problems indicated by the Lisp implementation, you're at the mercy of the Lisp implementor.

What restarts are made available and when are rarely (if ever) defined in the standard. So I thought it might be worthwhile to provide a partial survey of situations and whether an implementation provides the continue restart (or something similar, such as retry) so that you can correct the situation and continue, either interactively or within a program.

SituationLisp implementations0
Allegro CLLispworksSBCLCLISP
No function defined1-
Failed function lookup2--
No class found--
Division by zero---
No method found3-
No slot found3---
Replace function with generic function-
Redefine a generic function4--

0 The versions tested were ACL 8.1 Enterprise, Lispworks 5.0 Personal, SBCL 1.0.13.53, and CLISP 2.4.

1 Use case: evaluate a form such as (foo 0) where foo is not fbound.

2 Evaluate (function foo) where foo is not fbound.

3 In these situations, it is not necessary to offer a continuable restart, since there are ways to deal with this using the MOP.

4 Use case: define a generic function, then evaluate some defmethod forms and make an incompatible change to the generic function definition using either defgeneric or defmethod. Alternatively, define a method without a generic function definition, then make an incompatible change to generic function.

If an implementation doesn't offer a restart for SETF functions (which usually means it doesn't offer one for function lookup), you can add your own by customizing *macroexpand-hook* to wrap the expansion in something that provides a way to continue, perhaps like the following, assuming that the (setf (foo (list 1)) 2) is replaced with the actual expanded form.

(block #1=#:BLOCK-1000
  (tagbody
     #2=#:START-1001
     (handler-bind
         ((undefined-function #'(lambda (#3=#:CONDITION-1002)
                                  (cerror (format nil "Try calling ~A again."
                                                  (cell-error-name #3#))
                                           "~A is not fbound."
                                           (cell-error-name #3#))
                                  (go #2#))))
        (return-from #1# (setf (foo (list 1)) 2)))))

Doing this for everything that is macroexpanded would be a bit much, so you should probably only do it in certain circumstances.

Currently, these are the only situations I'm concerned with. Well, almost — I threw in the division by zero one since it's a canonical example of an error.

It's hard not to notice that the commercial implementations listed offer a lot more in the way of restarts than the free ones listed. I'm sure there is a reason for this, probably related to customer demands. I will say that the restarts offered by Allegro and LispWorks have caused me to start using them instead of SBCL. Doing what I want to do in SBCL requires a lot more hackery (either changing SBCL itself or using some techniques I'd rather not use). Right now, I'm using Allegro CL Enterprise since I got an equipment grant for it with AllegroGraph, but LispWorks is looking pretty good too.

7 comments:

Matt said...

I have pleaded to get a use-value
restart into sbcl in the context of:

http://groups.google.com/group/comp.lang.lisp/browse_frm/thread/c9f1b52e16f48fec

and

http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/e3a45043c461c66a

However, I was mostly alone, or not loud enough.

Alastair Bridgewater said...

Seeing this, I remembered that I had done some work towards this:

http://article.gmane.org/gmane.lisp.steel-bank.devel/6433

The two main arguments that I see for this or something similar not being checked in are:

1.) The backend hacking required to support more architectures than just x86.

2.) The nastiness done to src/code/interr.lisp.

Were I to float a patch for this again, I'd revisit #2, and I'd make the code in interr.lisp conditional not on platform, but on the CONTINUABLE argument passed to INTERNAL-ERROR.

That said, as this is a feature that I don't need, and requires backend hacking for platforms I don't have, I am disinclined to fight for it very hard.

Anonymous said...

Nice review, but clisp 2.4? Is it a typo, or something really old?

Andreas Davour said...

groan

So it's that bad for the free implementations?

I really wish sbcl could catch up a bit. I really miss this feature.

Generic Viagra said...

As a newbie programmer, I just have programed on Java, so I would like to know what is the difference if I want to program on Lisp

Generic Viagra said...

this is main problem at the hour of programming something by yourself, you have the search the best software to do this, in my I use simple programs to left a open code to any modification that can improve the performance of the same.
Buy Viagra Cheap Viagra.

extreme bondage video said...

I appreciate this post.